Rumah > hujung hadapan web > tutorial js > typescript指南详解

typescript指南详解

零下一度
Lepaskan: 2017-06-30 10:48:24
asal
2945 orang telah melayarinya

 

typescript指南

前言

typescript是angularjs2推荐使用的脚本语言。它由微软2012年首次发布。 

一.    typescript和javascript的区别

1.从遵循的规范上的角度:

  Javascript遵循ECMA5的规范,TypeScript是语法上对ECMA6的实现。

2.从功能上说:

TypeScript提供了类、模块和接口来帮助构建组件,更方便写面向对象的程序,所以被称为更好的typescript。

3.从支持上说:

所有的浏览器都支持ES5及之前的javascript,而没有浏览器支持typescript,所以需要编译。另外typescript支持所有javascript的语法。

照片显示了ES5、ES2015、ES2016以及typescript的关系。

二. typescript的安装和helloworld程序

(1)npm安装

1

 npm install -g typescript@1.8 全局安装了1.8版本的typescript编译器和tsc程序,并且添加到环境变量中。为了确认正常,打印-v命令。

Salin selepas log masuk

1

$ npm -v   //首先看一下npm是不是新版本$ npm install -g typescript@1.8. //全局安装typescript$ tsc -v

Salin selepas log masuk

显示typescript的版本是:

(2)运行第一个ts程序。

第一种方式。

首先在1.hello.ts程序中输入:

1

console.log("hello world!")

Salin selepas log masuk

然后执行命令:

1

$ tsc 1.hello.ts  //把ts文件转换为等价的hello.js

Salin selepas log masuk

最后运行node来执行js文件。

1

$ node 1.hello.js //打印出hello world。成功!

Salin selepas log masuk

第二种方式。

先装好typescript和ts-node.

1

$ npm install typescript@1.8  -g    //装好ts$ npm install  -g ts-node //装ts-node模块,选项是-g哦。

Salin selepas log masuk

再运行命令执行文件。

1

$ ts-node 1.hello.ts //打印hello world,成功!

Salin selepas log masuk

三. typescript从ES2015和ES2016引入的语法和特性

ES2015和ES2016有比较大的变化。

(1)ES2015的箭头函数

由来:javascript有一级函数的特性,也就是说,函数能够当做参数进行传参,很好的用法不过太啰嗦,所以ES2015提供了箭头函数,更加简洁。

ts例子1:

1

var even=[3,1,56,7].filter(el=>!(el%2));//对数组调用过滤器filter函数,!(el%2)只有偶数的取模操作为0,那么整体就是true,所以el获取的是偶数console.log(even);//打印56咯。

Salin selepas log masuk

通过运行ts-node命令,屏幕显示结果为:

ts例子2:

1

2

var result=[1,2,3]

    .reduce((total,current)=>total+current,0);//reduce对每个数组元素比如1、2、3调用回调函数,初始值为0。//回调函数的参数是total和current,total是上一次回调函数的值,第一次就为初始值0.// current是当前数组元素的值。返回值是total+current。//那么就清晰了,第一个total为0,当前元素是1,返回的0+1.console.log(result);

Salin selepas log masuk

通过运行ts-node命令,屏幕显示结果为:

ts例子3:

1

2

3

4

5

6

7

8

var data=[

    {price:10,total:70},

    {price:94,total:340},

    {price:14,total:34}

];var sorted=data.sort((a,b)=>{//a、b为依次遍历的数组元素//这里的对象是函数体var diff=a.price-b.price;if(diff!==0){return diff;

    }return a.total-b.total;

})

console.log(sorted);

Salin selepas log masuk

通过运行ts-node命令(编译),屏幕显示正确的排序结果为:

ts例子4:

1

2

3

4

5

function MyComponent(){this.age=42;

    console.log(this);

    setTimeout(()=>{//箭头函数的特性,它的执行上下文指向外层的代码,即组件实例的this对象this.age+=1;

        console.log(this.age);

    },100);//等待100ms,age加1等于43,并打印出来}new MyComponent();

Salin selepas log masuk

通过编译,屏幕显示:

(2)ES2015和ES2016中的类

首先说明一下,ES6就是ES2015。ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。

然后ECMAScript 2016就是ES7。

ES6的类依然是使用构造函数和基于原型的继承,但是语法更加方便和简洁。

下面是ES2016定义类的语法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

class Human{

    static totalPeople=0;

    _name;//ES2016的属性定义语法    constructor(name){this._name=name;

        Human.totalPeople+=1;

    }

    get name(){return this._name;

    }

    set name(val){this._name=val;

    }//name属性的get和set方法    talk(){return "Hi,I'm"+this.name;

    }

}

class Developer extends Human{

    _languages;//ES2016的属性语法    constructor(name,languages){

        super(name);this._languages=languages;

    }

    get languages(){return this._languages;

    }

    talk(){return super.talk()+" And I Know "+this.languages.join('.');

 

    }

}var human=new Human("foobar");var dev=new Developer("bar",["javascript"]);

console.log(dev.talk());

Salin selepas log masuk

喜欢!除了类的定义体是对象外,属性和方法的书写和java很像,我喜欢的方式。

 通过编译,屏幕显示结果为:

(3)定义块级作用域中可见的变量

java和c++是块级作用域。

只列举代码,表现为2点,第一个是特定代码块的变量只能代码块内部可见,第二,嵌套在内部的代码块中也可见。

1

2

public class Demo{// 属性块,在类初始化属性时候运行    {int j = 2;// 块级变量    }public void test1() {int j = 3;  // 方法级变量if(j == 3) {//j变量在代码嵌套的内部if语句中可见int k = 5;  // 块级变量        }

}

Salin selepas log masuk

javascript是函数作用域。

1

2

3

4

5

6

var fns=[];for(var i=0;i<5;i+=1){

    fns.push(function(){

        console.log(i);

    })

}

fns.forEach(fn=>fn());

Salin selepas log masuk

打印结果,很奇怪。

ES6代码:

1

2

3

4

5

6

var fns=[];for(let i=0;i<5;i+=1){

    fns.push(function(){

        console.log(i);

    })

}

fns.forEach(fn=>fn());

Salin selepas log masuk

打印结果:

(4)使用ES2016装饰器进行元编程。

装饰器是ES2016的一个提案,它的依据是“在设计阶段可以对类和属性进行注释和修改”。在angular2很常用,可用来定义组件、指令以及管道,并且还能配合依赖注入机制来使用。

首先,装饰器能干吗。

装饰器典型的用法是把方法和属性标记为过期,另一个应用场景是声明式语法,从而实现面向切面的编程。其实呢,装饰器只是一个语法糖而已。装饰器目前并没有得到真正的使用

1

Experimental support for decorators is a feature that is subject to change in a future release.

Salin selepas log masuk

1

2

3

4

5

6

7

8

//编写了Person类,它只有一个getter,名字为kidCountclass Person{//kidCount有一个装饰器nonenumerable    @nonenumerable

    get kidCount(){return 42;

    }

}//装饰器函数接收3个参数,最后返回descriptor属性。function nonenumerable(target,name,descriptor){

    descriptor.enumerable=false;//可枚举性return descriptor;

}var person=new Person();for(let prop in person){

    console.log(prop);

}

Salin selepas log masuk

对应的ES5语法类似于:

1

descriptor=nonenumerable(Person.prototype,'kidCount',descriptor);//descriptorObject.defineProperty(Person.prototype,'kidCount',descriptor);

Salin selepas log masuk

接下来,介绍angular 2装饰器的用法。

1

"app""./app.html""/",component:Home,name:'home'"/",component:About,name:'about'

Salin selepas log masuk

如果装饰器需要接收参数,那么就定义为接收参数的函数,然后由函数返回真正的装饰器。

 (5)使用ES2015编写模块化的代码

angular 1.x引入了一套模块系统,不过并不支持懒加载特性。angular 2种充分利用了ES2015提供的模块系统。ES2015提供了声明式API,以及使用模块加载器的命令式API。

语法分为export和import两个方面。

第一,看一个简单的DEMO:

math.ts:

1

2

3

4

5

export function square(x){return Math.pow(x,2);

}

export function log(x){return Math.log(x);

}

export const PI=Math.PI;

Salin selepas log masuk

math2.ts,更简洁的写法而已:

1

2

3

4

5

function square(x){return Math.pow(x,2);

}function log(x){return Math.log(x);

}

const PI=Math.PI;

export {square,log,PI}

Salin selepas log masuk

app.ts调用,要编译的是app.ts哦:

1

2

3

import {square,log} from "./math";

console.log(square(2));

console.log(log(100));

Salin selepas log masuk

屏幕显示效果:

第二,ES2015模块化语法带有隐式的异步行为。

比如说,

A模块依赖于B、C模块。当用户请求A模块,JS模块加载器会先加载B和C模块,才能调用A模块。这里B和C模块都是异步加载的。

第三,典型的应用场景会给导出的内容起一个名字。

使用别名导入整个模块的DEMO:

1

2

import * as math from "./math";//as语法咯console.log(math.square(2));

console.log(math.log(100));

Salin selepas log masuk

第四,默认导出。

模块导出使用了export default语法,是一种带名字的导出。

基本的默认导出DEMO:

math3.ts:

1

2

3

export default function cube(x){return Math.pow(x,3);//默认导出的名字是cube}

export function square(x){return Math.pow(x,2);

}

Salin selepas log masuk
Salin selepas log masuk

app3.ts:

1

import cube from "./math3";//等同于import {default as cube} from "./math3console.log(cube(3));

Salin selepas log masuk

显示结果正常:

默认导出混合其他导出的DEMO:

math3.ts:

1

2

3

export default function cube(x){return Math.pow(x,3);//默认导出的名字是cube}

export function square(x){return Math.pow(x,2);

}

Salin selepas log masuk
Salin selepas log masuk
app4.ts:

1

2

3

import cube,{square} from "./math3";

console.log(square(2));

console.log(cube(3));

Salin selepas log masuk

显示结果OK:

 (6)ES2015的模块加载器

通过编程的方式加载app模块执行main函数,使用System对象的import方法就好了。现在代码因为缺乏配置项,所以还运行不起来。

app.ts:

1

2

3

export function main(){

    console.log(2);

}

Salin selepas log masuk

init.js

1

2

3

4

5

6

7

System.import("./app")

.then(app=>{

    app.main();

})

.catch(error=>{

   console.log("致命的错误"); 

});

Salin selepas log masuk

四: 发挥静态类型的优势

有了静态类型,那么IDE开发环境除了避免输入错误的语法高亮,还提供精确静态分析的建议。很棒。

typescript的所有类型包含几类:

● 原生类型

● 组合类型

● Object类型

● 泛型

● any类型

(1)使用显式类型定义

除了webstorm报类型(type)错误,运行编译命令,typescript  也报错 Type 'string' is not assignable to type 'number' 。那么就是说,一旦foo设置了类型,就不能赋值为其他类型了。

(2)any类型

any类型是所有其他类型的父类,代表可以拥有任何类型的值,类似于动态类型,一方面不会报错,另一方面则放弃了typescript的优点了。

1

2

3

4

5

let foo:any;

foo={};

foo="bar";

foo+=24;

console.log(foo);//结果为"bar 24"。

Salin selepas log masuk

(3)原生类型

就是javascript比较熟悉的Number、String、Boolean、Null、以及Undefined。而Enum是用户自定义的原生类型,它是Number的子类。含义是枚举用户自定义的类型,由一系列有名称的值也就是元素构成。

定义enum如下:

1

2

3

4

5

enum STATES{

    CONNECTING,

    WAITING,

    CONNECTED

}//定义枚举类型if(this.state==STATES.CONNECTING){//通过点语法引入}

Salin selepas log masuk

(4)Object类型

首先,讲更加通用的Array类型,它是Object的子类。

Typescript的数组,要求元素类型相同。都可以使用js的各种数组方法,比如push、join、splice等,也可以使用方括号运算符对数组元素进行访问。

数值型数组DEMO:

1

2

3

4

let primes:number[]=[];

primes.push(2);

primes.push(3);

console.log(primes);

Salin selepas log masuk

any型数组:

1

2

3

4

5

let randomItems:any[]=[];

randomItems.push(1);

randomItems.push("foo");

randomItems.push("{}");

console.log(randomItems);

Salin selepas log masuk

屏幕结果为

第二,说Function类型,也是Object的子类哦。

javascript有两种方式创建新函数:

1

2

3

4

5

//函数表达式var isPrime=function(n){

 

}//函数声明function isPrime(n){

    

}//或者,使用箭头函数var isPrime=n=>{   //函数体}

Salin selepas log masuk

Typescript增加的是参数和返回值的类型。

函数表达式:

1

let isPrime:(n:number)=>boolean=n=>{//整个函数赋给变量isPrime,参数是number类型的n,返回值是boolean类型的n,函数体在{}里面}

Salin selepas log masuk

函数声明:

1

function isPrime(n:number):boolean{//参数为number类型,返回值为boolean类型}

Salin selepas log masuk

对象字面量的定义写法:

1

2

3

4

5

let person={

     _name:null,

     setName(name:string):void{       //参数是string类型,返回值是void   this._name=name;

     }

}

Salin selepas log masuk

(5)定义类

typescript定义类,属性的声明式强类型的。

 class-basic.ts:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

class Human {

    static totalPeople=0;

    _name:string;//强类型的哦    constructor(name){      this._name=name;

      Human.totalPeople+=1;

    };

    get name(){return this.name;

    }

    set name(val){this._name=val;

    }

    talk(){return "HI,I'am"+this._name;

    }

}

let human=new Human("foo");

console.log(human._name);

Salin selepas log masuk

打印结果为

成功

(6)访问修饰符

有3个。更好的实现封装和更优雅的接口。

●public。public的属性和方法在任何地方可以访问。

●private。private的属性和方法只能在类定义内部进行访问。

●protected。protected的属性和方法可以类定义内部访问,也可以从子类访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

//typescript实现class Human{

    static totalPeople=0;

    constructor(protected name:string,private age:number){//定義了一個protected型的屬性,名為name,類型為string//age屬性。好處是避免显示式的赋值操作Human.totalPeople+=1;

    }

    talk(){return "Hi,I'm"+this.name;

    }

}

class Developer extends Human{

    constructor(name:string,private languages:string[],age:number){//显式使用访问修饰符,或者定义接收的参数类型,都可以混合使用的。        super(name,age);

    }

    talk(){return super.talk()+" And I Know "+this.languages.join('.');

 

    }

}//创建developer类的一个新实例let Dev=new Developer("foo",["javascript","Go"],42);//dev.languages=["java"];这行代码会报一个私有属性的错误let human=new Human("foo",42);//human.age=42;由于私有属性,所以报错//human.name="bar",会报一个protected错误。它只能在类类内部或者子类中访问

Salin selepas log masuk

(6)接口

接口定义了一组对象共同的属性和方法,称作接口签名。要实现接口,那么实现定义接口规定的所有属性和方法。

关键字就2个,interface和implements。

1

2

3

4

5

6

7

8

9

10

11

interface Accountable1{

    goIncome():number;

    accountNumber:string

}

class Value implements Accountable1{

    constructor( public accountNumber:string){this.accountNumber=accountNumber;

    }

    goIncome():number{return 100;

    }

}var extra=new Value("余额");

console.log(extra.accountNumber);

Salin selepas log masuk

打印为

接口实现,成功

接口继承

接口之间可以互相继承。接口继承另外一个接口,使用extends关键字。

interface-extends.ts:

1

2

3

4

5

6

7

interface Accountable2{

    accountNumber:string;

    getIncome():number;

}

interface Individual extends Accountable2{

    ssn:string;

}

Salin selepas log masuk

实现多个接口:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

interface People{

    age:number;

    name:string;

}

interface Accountable{

    accountNumber:string;

    goIncome():number

}

class Person implements People,Accountable{  //实现多个接口,用逗号分隔    age:number;

    name:string;

    accountNumber:string;

    constructor(age:number,name:string,accountNumber:string){

 

    }

    goIncome():number{return 10;

    }

}var person=new Person(10,"100","1000");

Salin selepas log masuk

五: 使用类型参数编写泛型代码

 可以写类似java的泛型代码,好精彩!使用“”。能写出更简洁的代码来。

(1)使用泛型类

定义一组类型的class。

1

2

3

4

5

6

7

8

9

10

class Nodes<T> {

    value:T;

    left:Nodes<T>;

    right:Nodes<T>;

}

let numberNode=new Nodes<number>();

 

let stringNode=new Nodes<string>();

 

numberNode.right=new Nodes<number>();//类型匹配numberNode.value=42;//类型匹配//numberNode.value="42";报错,类型不匹配//numberNode.left=stringNode;报错,类型不匹配

Salin selepas log masuk

(2)使用泛型函数

定义一组类型的函数。

1

2

3

4

5

function identify<T>(arg:T){return arg;

}

interface Comparable{

    compare(a:Comparable):number;

}function sort<I extends Comparable>(arr:Comparable[]){//}

Salin selepas log masuk

(3)多重泛型

1

2

3

4

5

6

7

class Pair<K,V>{

    key:K;

    value:V;

}

let pair=new Pair<string,number>()

pair.key="foo";

pair.value=42;

Salin selepas log masuk

精彩

五: 使用typescript的类型推断机制简化代码

typescript可以猜测代码中的静态类型。好智能哦,所以可以用来省略一些代码。

1

2

3

4

let answer=42;

answer="42";//会提示错误,因为一开始的赋值,typescript已经把它当做number类型了。let answer;

answer=42;

answer="42";//这个时候不会报错,因为第一次声明时,typescript给到的静态类型是any。

Salin selepas log masuk

(1)最常见的类型

1

let x=["42",42];//x的类型推断为any[]数组。

Salin selepas log masuk

1

let x=[42.null,32];//typescript的类型推断为number[]。

Salin selepas log masuk

(2)与上下文有关的类型推断

 

这里可以看到e并没有规定类型,可是typescript就根据上下文推断它为MouseEvent鼠标事件。

六: 使用外部类型定义

尽管静态类型很酷,但是我们使用的大部分前端类库都是基于javascript构建的,都是动态类型。而typescript提供了额外的类型定义,来给编译器提供提示。

(1)使用预定义的外部类型定义

 step1:安装typings工具

1

npm install  -g  typings

Salin selepas log masuk

于是就安装好了typings目录,tsconfig.json、typings.json中,内容为:

1

2

3

4

//tsconfig.json{  "compilerOptions": {"module": "commonjs","target": "es5","sourceMap": true

  },  "exclude": ["node_modules"

  ]

}

Salin selepas log masuk

1

2

//typings.json{  "dependencies": {}

}

Salin selepas log masuk

step2:创建基础配置

1

typings init

Salin selepas log masuk

step3:搜索

1

$  typings search module

Salin selepas log masuk

step4:安装is-builtin-module

1

$  typings install is-builtin-module  --save

Salin selepas log masuk

这个时候,typings.json的内容变为

1

2

3

{  "dependencies": {"is-builtin-module""registry:npm/is-builtin-module#1.0.0+20161031191623"

  }

}

Salin selepas log masuk

typings目录变为

(2)自定义外部类型

step1:定义好类库的接口。

define-external-type.ts:

1

2

3

4

5

interface LibraryInterface{

    selectElements(selector:string):HTMLElement[];

    hide(element:HTMLElement):void;

    show(element:HTMLElement):void;

}

Salin selepas log masuk

step2:定义ts.d文件。

1

2

3

4

5

6

interface DOMLibraryInterface{

    selectElements(selector:string):HTMLElement[];

    hide(element:HTMLElement):void;

    show(element:HTMLElement):void;

}

declare var DOM:DOMLibraryInterface;

Salin selepas log masuk

 step3:DOM通过reference引入,编译器就会找到对应的外部类型定义了。

1

2

3

4

5

6

7

///<reference path="dom.d.ts" />var DOM={

    selectElements:function(selector:string):HTMLElement[]{return [];

    },

    hide:function(element:HTMLElement):void {

        element.hidden=true;

    }

};

Salin selepas log masuk

这个时候,会报错。直到完全实现定义的接口为止,如下:

1

2

3

4

5

6

7

8

9

10

///<reference path="dom.d.ts" />var DOM={

    selectElements:function(selector:string):HTMLElement[]{return [];

    },

    hide:function(element:HTMLElement):void {

        element.hidden=true;

    },

    show:function(element:HTMLElement):void{

        element.hidden=false;

    }

};

Salin selepas log masuk

这样分离出来,就可以把全部的外部类型定义放在同一个文件里,方便管理了。

Atas ialah kandungan terperinci typescript指南详解. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan