20160113

继续继续学习 TypeScript

复习前面的 accessors (存取器)

In this version, we check to make sure the user has a secret passcode available before we allow them to modify the employee. We do this by replacing the direct access to fullName with a ‘set’ that will check the passcode. We add a corresponding ‘get’ to allow the previous example to continue to work seamlessly.
在这个例子里,在允许修改 employee 之前,我们先检查密码是否正确。我们把 fullname 的直接访问变成了检查密码的 set 方法。我们也加了一个 get 方法,上述例子仍然可以工作。

1.var passcode="secret passcode";
2.class Employee{
3. private _fullname: string;
4.
5. get fullName():string {
6. return this._fullname;
7. }
8. set fullName(newName: string){
9. if(passcode && passcode === "secret passcode"){
10. this._fullname = newName;
11. }
12. else{
13. alert("error,incorrect password");
14. }
15. }
16.}
17.var employee = new Employee();
18.employee.fullName = "Bob Smith"; // 像是调用了 set 方法
19.if(employee.fullName){ // 像是调用了 get 方法
20. alert(employee.fullName);
21.}

To prove to ourselves that our accessor is now checking the passcode, we can modify the passcode and see that when it doesn’t match we instead get the alert box warning us we don’t have access to update the employee.
Note: Accessors require you to set the compiler to output ECMAScript 5.
我们可以修改密码,来证明存取器是否工作。当密码不对时,证明我们不能修改 employee。

  • Static Properties(静态属性)
    Up to this point, we’ve only talked about the instance members of the class, those that show up on the object when its instantiated. We can also create static members of a class, those that are visible on the class itself rather than on the instances. In this example, we use ‘static’ on the origin, as it’s a general value for all grids. Each instance accesses this value through prepending the name of the class. Similarly to prepending ‘this.’ in front of instance accesses, here we prepend ‘Grid.’ in front of static accesses.
    在前面的例子中,我们只讨论了类中实例化的成员,仅当类被实例化才会被初始化的属性。我们可以创建类的实例成员,这些成员存在于类本身不是类的实例化上。在这个例子中,我们用 static 定义 origin ,因为他是所有网格都具有的属性。每个实例想访问这个属性时,都要在前面加上类名。如同在实例属性前面加上 this. 来访问一样,我们使用 Grid. 来访问静态属性。
1.class Grid{
2. static origin = {x: 0,y: 0};
3. calculateDistanceFromOrigin(point: {x: number;y: number}){
4. var xDist = (point.x - Grid.origin.x);
5. var yDist = (point.y - Grid.origin.y);
6. return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
7. }
8. construstor(public scale: number);
9.}
10.var grid1 = new Grid(1.0);
11.var grid2 = new Grid(5.0);
12.alert(grid1.calculateDistanceFromOrigin({x: 10,y: 10}));
13.alert(grid2.calculateDistanceFromOrigin({x: 10,y: 10}));
  • Advanced Techniques(高级技巧)
    1. Constructor functions(构造函数)
      When you declare a class in TypeScript, you are actually creating multiple declarations at the same time. The first is the type of the instance of the class.
      当你在 TypeScript 里定义类的时候,同时定义了很多东西。首先是了类的实例的类型。
1.class Greeter{
2. greeting: string; // 定义实例化的成员
3. constructor(message: string){ // 构造函数
4. this.greeting = message;
5. }
6. greet(){ // 定义一个函数
7. return "Hello" + this.greeting;
8. }
9.}
10.var greeter: Greeter; // 给 Greeter 类一个实例
11.greeter = new Greeter("world");
12.alert(greeter.greet());

Here, when we say ‘var greeter: Greeter’, we’re using Greeter as the type of instances of the class Greeter. This is almost second nature to programmers from other object-oriented languages.
当我们写 var greeter: Greeter 我们给 Greeter 类一个实例。这对于面向对象的程序猿来说是老习惯了。

We’re also creating another value that we call the constructor function. This is the function that is called when we ‘new’ up instances of the class. To see what this looks like in practice, let’s take a look at the JavaScript created by the above example:
我们也创造了一个值当我们调用构造函数的时候。这个函数被调用当我们用 new 关键字创造函数的实例时。

1.var Greeter = (function(){
2. function Greeter(message){
3. // 定义一个函数
4. this.greeting = message;
5. }
6. // 在这个函数的原型链上定义函数
7. Greeter.prototype.greet = function(){
8. return "Hello, " + this.greeting;
9. }
10. return Greeter;
11.})(); // 自执行匿名函数
12.var greeter;
13.greeter = new Greeter("world");
14.alert(greeter.greet());

Here, ‘var Greeter’ is going to be assigned the constructor function. When we call ‘new’ and run this function, we get an instance of the class. The constructor function also contains all of the static members of the class. Another way to think of each class is that there is an instance side and a static side.
这里,var Greeter 是要指定的构造函数。当我们调用 new 去执行这个函数,我们获得一个类的实例化,这个构造函数也包括所有这个类的静态成员。换个角度说,我们可以说类具有实例和静态两个部分。
我们来改写这个例子:

1.class Greeter{
2. static standardGreeting = "Hello, there";
3. greeting: string;
4. greet(){
5. if(this.greeting){
6. return "Hello, " + this.greeting;
7. }
8. else{
9. Greeter.standardGreeting;
10. }
11. }
12.}
13.var greeter1: Greeter;
14.greeter1 = new Greeter();
15.alert(greeter1.greet());
16.
17.var greeterMaker : typeof Greeter = Greeter;
18.greeterMaker.standardGreeting = "Hey there!";
19.var greeter2: Greeter = new greeterMaker();
20.alert(greeter2.greet());

In this example, ‘greeter1’ works similarly to before. We instantiate the ‘Greeter’ class, and use this object. This we have seen before.
Next, we then use the class directly. Here we create a new variable called ‘greeterMaker’. This variable will hold the class itself, or said another way its constructor function. Here we use ‘typeof Greeter’, that is “give me the type of the Greeter class itself” rather than the instance type. Or, more precisely, “give me the type of the symbol called Greeter”, which is the type of the constructor function. This type will contain all of the static members of Greeter along with the constructor that creates instances of the Greeter class. We show this by using ‘new’ on ‘greeterMaker’, creating new instances of ‘Greeter’ and invoking them as before.
接下来,我们直接用了这个类,我们创建了一个新的变量叫做 greeterMaker。这个变量保存了这个类或者说保存了它的构造函数。接下来用 typeof Greeter ,就是去这个类的类型,而不是实例的类型。也就是说,告诉我 Greeter 标识符的类型,也就是构造函数的类型。这个类型将包含所有的静态成员和构造函数。然后,就和前面一样,我们在 greeterMaker 上使用 new ,创建 Greet 类型的实例。

  • Using a class as an interface(把类当做接口使用)
    As we said in the previous section, a class declaration creates two things: a type representing instances of the class and a constructor function. Because classes create types, you can use them in the same places you would be able to use interfaces.
    如同上一节所讲的,类会创建出实例类型和构造函数,正因为类能创建出实例类型,所以在一些情况下,类可以当做接口使用。
1.class Point{
2. x: number;
3. y: number;
4.}
5.interface Point3d extends Point{
6. z: number;
7.}
8.var point3d: Point3d = {x: 1,y: 2,z: 3};

Modules

This post outlines the various ways to organize your code using modules in TypeScript. We’ll be covering internal and external modules and we’ll discuss when each is appropriate and how to use them. We’ll also go over some advanced topics of how to use external modules, and address some common pitfalls when using modules in TypeScript.
这篇文章介绍了 TypeScript 组织代码的各种方法。包括命名空间和模块。我们也会谈他们的高级试用场景,以及他们使用过程中常见的陷阱。

  • First steps
    Let’s start with the program we’ll be using as our example throughout this page. We’ve written a small set of simplistic string validators, like you might use when checking a user’s input on a form in a webpage or checking the format of an externally-provided data file.
    我们将写一个例子并且在整篇文章中都使用,我们写一个字符串验证器,假设你会使用它来验证用户的输入或者验证用户数据。
1.interface StringValidator{
2. isAcceptable(s: string): boolean;
3.}
4.
5.var lettersRegexp = /^[A-Za-z]+$/; // 匹配所有英文字母
6.var numberRegexp = /^[0-9]+$/; // 匹配所有数字
7.
8.class letterOnlyValidator implements StringValidator{ // 声明一个类来实现这个接口
9. isAcceptable(s: string){ // 重写类中的方法
10. return lettersRegexp.test(s); // 返回传入的参数是否匹配这个正则表达式
11. }
12.}
13.
14.class ZipCodeValidator implements Vaildator{
15. isAcceptable(s: string){
16. return s.length===5 && numberRegexp.test(s);
17. // 如果传入字符串的长度为5,测试是不是符合正则表达式,返回测试结果
18. }
19.}
20.
21.var strings = ['Hello','98052','101'];
22.// [s: string] 声明为接口类型的?
23.// var validators 声明为对象类型,初始化为一个空对象
24.var validators : {[s: string]: StringValidator; } = {}; // ???
25.validators['ZIP Code'] = new ZIPCodeValidator();
26.validators['Letters Only'] = new letterOnlyValidator();
27.// show whether each string passed each validator
28.strings.forEach( s => {
29. for(var name in validators){ // 两重循环
30. console.log('"'+ s +'"' + (validators[name].isAcceptable(s) ? 'matches' : 'does not match') + name);
31. }
32.})
  • Adding Modularity(添加命名空间)
    As we add more validators, we’re going to want to have some kind of organization scheme so that we can keep track of our types and not worry about name collisions with other objects. Instead of putting lots of different names into the global namespace, let’s wrap up our objects into a module.
    In this example, we’ve moved all the Validator-related types into a module called Validation. Because we want the interfaces and classes here to be visible outside the module, we preface them with export. Conversely, the variables lettersRegexp and numberRegexp are implementation details, so they are left unexported and will not be visible to code outside the module. In the test code at the bottom of the file, we now need to qualify the names of the types when used outside the module, e.g. Validation.LettersOnlyValidator.
    随着更多验证器的加入,我们需要一种手段来组织代码,以便在记录他们类型的同时还不担心和其他对象产生命名冲突,因此,我们把验证器放到一个命名空间内而不是全局命名空间下。
    在这个例子中,我们把所有验证器相关的类型都放到一个叫做 Validator 的命名空间中。因为我们想让这些接口和类在命名空间之外也是可访问的,所以需要使用 export 。相反的,变量 lettersRegexp 和 numberRegexp 是实现的细节,不需要导出,因此他们在命名空间之外的地方是不能访问的。在文末尾的测试代码中,在我们在命名空间外访问时,我们需要验证类型的名称。就像是 Validation.LettersOnlyValidator 。
1.module Validation {    // 定义一个命名空间
2. export interface StringValidator{ // 接口在命名空间之外也是可显示的
3. isAcceptable(s: stirng): boolean;
4. }
5. var lettersRegexp = /^[A-Za-z]+$/;
6. var numberRegexp = /^[0-9]+$/;
7. export class letterOnlyValidator implements StringValidator{
8. isAcceptable(s: string){
9. return lettersRegexp.test(s); // 正则表达式是否匹配s
10. }
11. }
12. export class ZipCodeValidator implements StringValidator{
13. isAcceptable(s: string){
14. return s.length===5 && numberRegexp.test(s);
15. }
16. }
17.}
18.var strings = ['Hello','12345','123'];
19.var validators = {[s: string]: Validation.StringValidator; } = {};
20.validators['ZIP code'] = new Validation.ZIPCodeValidator();
21.validators['Letters only'] = new Validation.LettersOnlyValidator();
22.strings.forEach(s => {
23. for(var name in validators){
24. console.log("'"+ s +"'" + (vallidators[name].isAcceptable(s) ? 'matches' : 'does not match') + name);
25. }
26.})

将内容都放到一个命名空间里,在需要使用时带上命名空间的前缀。

  • Splitting Across Files 分离到多文件
    As our application grows, we’ll want to split the code across multiple files to make it easier to maintain.
    Here, we’ve split our Validation module across many files. Even though the files are separate, they can each contribute to the same module and can be consumed as if they were all defined in one place. Because there are dependencies between files, we’ve added reference tags to tell the compiler about the relationships between the files. Our test code is otherwise unchanged.
    当我们的应用越来越大时,我们需要将代码分离到不同的文件以便于维护。
    这里,我们分开 Validation 命名空间在不同的文件。尽管这些文件是分开的,它们仍是同一个命名空间。并且在使用是就像是它们是在一个文件中定义的一样。因为不同文件之间存在依赖关系,所以我们用标签来说明它们之间的关联。我们的测试代码保持不变。
    1. Multi-file internal modules(多文件中的命名空间)
1.// Validation.ts
2.module Validation {
3. export interface StringValidator {
4. isAcceptable(s: string): boolean;
5. }
6.}
7.// LettersOnlyValidator.ts
8./// <reference path="Validation.ts" />
9.module Validation {
10. var lettersRegexp = /^[A-Za-z]+$/;
11. export class LettersOnlyValidator implements StringValidator {
12. isAcceptable(s: string) {
13. return lettersRegexp.test(s);
14. }
15. }
16.}
17.// ZipCodeValidator.ts
18./// <reference path="Validation.ts" />
19.module Validation {
20. var numberRegexp = /^[0-9]+$/;
21. export class ZipCodeValidator implements StringValidator {
22. isAcceptable(s: string) {
23. return s.length === 5 && numberRegexp.test(s);
24. }
25. }
26.}
27.// Test.ts
28./// <reference path="Validation.ts" />
29./// <reference path="LettersOnlyValidator.ts" />
30./// <reference path="ZipCodeValidator.ts" />
31.
32.// Some samples to try
33.var strings = ['Hello', '98052', '101'];
34.// Validators to use
35.var validators: { [s: string]: Validation.StringValidator; } = {};
36.validators['ZIP code'] = new Validation.ZipCodeValidator();
37.validators['Letters only'] = new Validation.LettersOnlyValidator();
38.// Show whether each string passed each validator
39.strings.forEach(s => {
40. for (var name in validators) {
41. console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
42. }
43.});

Once there are multiple files involved, we’ll need to make sure all of the compiled code gets loaded. There are two ways of doing this.
一旦有多个文件,我们需要确保所有编译后的代码都被加载了,有两种方法。
第一种方式,把所有的输入文件编译为一个输出文件,需要使用–outFile标记:

1.tsc --out sample.js Test.ts

编译器会根据源码里的引用标签自动地对输出进行排序。你也可以单独地指定每个文件。

1.tsc --out sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts

第二种方式,我们可以编译每一个文件(默认方式),那么每个源文件都会对应生成一个JavaScript文件。 然后,在页面上通过 script 标签把所有生成的JavaScript文件按正确的顺序引进来,比如:

1.    <script src="Validation.js" type="text/javascript" />
2. <script src="LettersOnlyValidator.js" type="text/javascript" />
3. <script src="ZipCodeValidator.js" type="text/javascript" />
4. <script src="Test.js" type="text/javascript" />

将每一个 TypeScript 编译为 JavaScript 文件,这样也可以学到 JavaScript 啊。自己编译完学习它的写法,闭包原型链,this,作用域。

  • Going External(别名)
    TypeScript also has the concept of an external module. External modules are used in two cases: node.js and require.js. Applications not using node.js or require.js do not need to use external modules and can best be organized using the internal module concept outlined above.
    TypeScript 也有外部模块的概念。外部模块在两种情况下使用:node.js 和 require.js,应用不使用 node.js 或者 require.js 不用使用外部模块并且可以用内部命名空间被搭好。

竟然在面试的时候说过学习 JavaScript 是把 JavaScript 高级程序设计的书看了两遍,现在想起来好搞笑。。
不用说学过什么新的东西,直接讲做出了什么就行了。就是学一点就要去做东西,这样才能遇到问题。

模块后面还有很多东西只是看了一遍,并没有仔细学习

函数

  • 书写完整函数类型
1.// 上面一行参数类型和返回值类型都是必须的
2.// 下面一行一般不写返回值类型,如果写了上面参数类型也可以不写
3.var myAdd: (x:number, y:number) => number =
4. function(x: number, y: number): number { return x+y; };
  • 可选参数和默认参数
    传递给一个函数的参数个数必须与函数期望的参数个数一致。
    JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined。 在TypeScript里我们可以在参数名旁使用?实现可选参数的功能。 比如,我们想让last name是可选的:
1.function buildName(firstName: string, lastName?: string) {
2. if (lastName)
3. return firstName + " " + lastName;
4. else
5. return firstName;
6.}
7.
8.var result1 = buildName("Bob"); //works correctly now
9.var result2 = buildName("Bob", "Adams", "Sr."); //error, too many parameters
10.var result3 = buildName("Bob", "Adams"); //ah, just right

可选参数必须跟在必须参数后面。 如果上例我们想让first name是可选的,那么就必须调整它们的位置,把first name放在后面。
在TypeScript里,我们也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是undefined时。 它们叫做有默认初始化值的参数。 让我们修改上例,把last name的默认值设置为”Smith”。

1.function buildName(firstName: string, lastName = "Smith") {
2. return firstName + " " + lastName;
3.}
4.
5.var result1 = buildName("Bob"); //works correctly now, also
6.var result2 = buildName("Bob", "Adams", "Sr."); //error, too many parameters
7.var result3 = buildName("Bob", "Adams"); //ah, just right
  • 剩余参数
    剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号(…)后面给定的名字,你可以在函数体内使用这个数组。
  • Lambda表达式和使用this
    我们把函数表达式变为使用lambda表达式( () => {} )。 这样就会在函数创建的时候就指定了‘this’值,而不是在函数调用的时候。
1.var deck = {
2. suits: ["hearts", "spades", "clubs", "diamonds"],
3. cards: Array(52),
4. createCardPicker: function() {
5. // Notice: the line below is now a lambda, allowing us to capture 'this' earlier
6. return () => {
7. var pickedCard = Math.floor(Math.random() * 52);
8. var pickedSuit = Math.floor(pickedCard / 13);
9.
10. return {suit: this.suits[pickedSuit], card: pickedCard % 13};
11. }
12. }
13.}
14.
15.var cardPicker = deck.createCardPicker();
16.var pickedCard = cardPicker();
17.
18.alert("card: " + pickedCard.card + " of " + pickedCard.suit);
19.
  • 重载(没看完)
    JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。
    为了让编译器能够选择正确的检查类型,它与JavaScript里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。
    注意,function pickCard(x): any并不是重载列表的一部分,因此这里只有两个重载:一个是接收对象另一个接收数字。 以其它参数调用pickCard会产生错误。