1.1 Typescript 介绍
1.TypeScript 是由微软开发的一款开源的编程语言,像后端 java、C#这样的面向对象语言可以让 js 开发大型企业项目。
2.TypeScript 是 Javascript的超级,遵循最新的 ES6、Es5 规范(相当于包含了es6、es5的语法)。TypeScript扩展了JavaScript的语法。
3.最新的 Vue 、React 也可以集成 TypeScript。
1.2 Typescript 安装 编译
安装nodejs环境,用npm全局安装typescriptnpm install -g typescriptTypescript文件后缀名为.ts,最后将编译成js文件
Typescript手动编译 => tsc + 文件名
// 将 index.ts编译成 index.js tsc index.ts
1.3 Typescript开发工具Vscode自动编译.ts 文件
1.3.1 tsc --init 生成配置文件tsconfig.json
1.3.2 点击菜单栏任务-运行任务(遇到错误使用快捷键ctrl + shift + b),点击 tsc:监视-tsconfig.json 然后就可以自动生成代码
了
typescript中为了使编写的代码更规范,更有利于维护,增加了类型校验
2.1 基础类型
在typescript中主要给我们提供了以下数据类型:
布尔类型(boolean)数字类型(number)
字符串类型(string)
数组类型(array)
元组类型(tuple)
枚举类型(enum)
任意类型(any)
null和undefined
void类型
never类型
相比于js,typescript中多了枚举类型、任意类型、void类型和never类型
2.2 变量定义
写ts代码变量必须指定类型,指定类型后赋值必须为指定的类型,否则报错
var flag:boolean = true flag = 123 // 错误,类型不一致
2.3 数据类型
布尔类型(boolean)var flag:boolean = true flag = false // 正确 // flag=123; // 错误数字类型(number)
var num:number = 123; num = 456; // 正确 // num='str'; //错误字符串类型(string)
var str:string = 'this is ts'; str= 'haha'; //正确 // str=true; //错误数组类型(array) ts中定义数组有两种方式
// 第一种 var arr:number[] = [ 1, 2, 3] // 第二种 var arr2: Array<number> = [ 1, 2, 3]元组类型(tuple)元素的类型不必相同,写法和数组一样
let arr:[number,string] = [ 123, 'this is ts']枚举类型(enum)
用法:
enum 枚举名{ 标识符[=整型常数], 标识符[=整型常数], ... 标识符[=整型常数], }
enum Flag {success = 1,error = 2}; let s:Flag = Flag.success // 使用枚举类型中的值 console.log( '正确状态',s) let f:Flag = Flag.error console.log( '错误状态',f)任意类型(any)
为那些在编程阶段还不清楚类型的变量指定一个类型
var number:any = 123 number = 'str' number = true2.9 null 和 undefined
undefined:
{ // 在js中,变量已声明但未初始化为undefined var undefinedTest:number // console.log(undefinedTest) // 错误写法,typescript报错,赋值了才正确 // 在typescript中,已声明未初始化的值要直接访问的话类型需要定义为undefined var undefinedTest2: undefined console.log(undefinedTest2) // 正确写法,输出undefined } { // 可能是number类型 可能是undefined var undefinedTest3:number | undefined; console.log(num); }
null:
// null是一个空指针对象,undefined是未初始化的变量。因此,可以把undefined看作是空的变量,而null看作是空的对象 var nullTest: null nullTest = null // nullTest = {} // 错误,定义了类型是null,值必须为nullvoid类型
typescript中的void表示没有任何类型,一般用于定义方法的时候方法没有返回值。
// 表示方法没有返回任何类型 function run( ): void { console.log( 'run') } run()never类型 表示的是那些永不存在的值的类型,例如异常
var a:never // a = 123 //错误写法 a = ( () => { throw new Error( '错误'); })()
三、Typescript函数
内容概述: 函数的定义、可选参数、默认参数、剩余参数、函数重载、箭头函数。
3.1.1 函数的定义
es5定义函数有函数声明法和匿名函数法// 法一:函数声明法 function run( ): string { return 'run' } /** // 错误写法 function run():string { return 123 } */ // 法二:匿名函数 var run2 = function ( ): string { return 'run2' } console.log( '函数定义一', run()) console.log( '函数定义二', run2())
3.1.2 ts中定义方法传参
函数传参要指定数据类型function paramFuc( name:string, age:number): string{ return ` ${name} --- ${age}` } console.log( '函数传参', paramFuc( 'dz', 20))
3.1.3 函数没有返回值的方法用void
function voidFnc( ): void{ console.log( '没有返回值的方法用void') } voidFnc();
3.2 可选参数
es5里面方法的实参和行参可以不一样,但是ts中必须一样,如果不一样就需要在可选参数后加?,这就是可选参数。function electParam( name:string, age?:number): string { // 这里的age可传可不传,age就是可选参数 if(age){ return ` ${name} --- ${age}` } else{ return ` ${name} --- 年龄保密` } } console.log( '可选参数', electParam( 'dz')) // 注意: 可选参数必须配置到参数的最后面 // 错误写法:可选参数不在最后面 // function electParam2(name?: string, age: number): string { // ... // }
3.3 默认参数
es5里面没法设置默认参数,es6和ts中都可以设置默认参数// age为默认参数 function defaultParam( name:string, age:number = 20): String { return ` ${name} --- ${age}` } console.log( '默认参数', defaultParam( 'dz'))
3.4 剩余参数
当有很多参数时候或参数个数不确定,可以用三点运算符// sum参数传过来的是一个数组 function sum( ...result: number[]): number { var sum = 0; for ( var i = 0; i < result.length; i++) { sum += result[i]; } return sum; } console.log( '剩余参数', sum( 1, 2, 3, 4, 5, 6)); // a=1 b=2 其他参数为剩余参数 function sum2( a: number, b: number, ...result: number[]): number { var sum = a * b; for ( var i = 0; i < result.length; i++) { sum += result[i]; } return sum; } console.log( '剩余参数2', sum2( 1, 2, 3, 4, 5, 6));
3.5 ts函数重载
同样的函数,传入不同的参数,实现不同的功能 java中方法的重载:重载指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。 typescript中的重载:通过为同一个函数提供多个函数类型定义来实现多种功能的目的。 ts为了兼容es5 以及 es6 重载的写法和java中有区别。// es5中同名函数,后面会覆盖前面的函数,ts中则不会 => 函数重载 function getInfo( name:string): string function getInfo( name:string, age:number): string function getInfo( name:any, age?:any): any { if(age) { return '姓名:' + name + '年龄:' + age } else{ return '姓名:' + name } } console.log(getInfo( 'dz')) console.log(getInfo( 'dz', 20)) // console.log(getInfo(20)) // 错误
3.6 箭头函数
箭头函数和es6中一样
setTimeout( () => { console.log( '箭头函数') }, 1000);
四、Typescript中的类
4.1 es5中的类
内容概述:类的创建、静态方法、继承(对象冒充继承,原型链继承,对象冒充 + 原型链组合继承)
es5中的面向对象、构造函数、原型与原型链本质可以看这个文档 http://caibaojian.com/javascr... , 个人觉得写得很清晰。
4.1.1 类的创建
es5类在构造函数和原型链里都可以添加属性和方法,原型链上的属性会被多个实例所共享,而构造函数则不会。
function Person( ) { this.name = 'Ming' this.run = function( ) { console.log( this.name + '在运动') } } Person.prototype.sex = '男' // 原型链上的属性会被多个实例所共享 Person.prototype.work = function( ) { console.log( this.name + '在工作') } var p = new Person() p.run() p.work() console.log(p.name)
4.1.2 静态方法
调用静态方法不需要实例化Person.getInfo= function( ){ console.log( '我是静态方法'); } Person.getInfo();
4.1.3 实现继承
对象冒充(或者叫构造函数继承)继承:可以继承构造函数里面的属性和方法,但是没法继承原型链上面的属性和方法原型继承:可以继承构造函数里面的属性和方法,也可以继承原型链上面的属性和方法,但是实例化子类的时候没法给父类传参
下面是通过 对象冒充 + 原型链 组合继承,解决了上面两种继承方式存在的问题
function Worker( name,age){ this.name=name; /*属性*/ this.age=age; this.run= function( ){ /*实例方法*/ alert( this.name+ '在运动'); } } Worker.prototype.sex= "男"; Worker.prototype.work= function( ){ alert( this.name+ '在工作'); } function Web( name,age){ Worker.call( this,name,age); // 对象冒充继承,可以继承构造函数里面的属性和方法,实例化子类可以给父类传参 } // Web.prototype = new Worker(); // 原型链继承方法一:继承Worker构造函数和原型上所有的方法和属性 Web.prototype = Worker.prototype; //原型链继承方法二:优化了方法一重复继承构造函数属性和方法的问题(本质可以看看http://caibaojian.com/javascript-object-5.html) var w = new Web( '赵四', 20); w.run(); w.work();
从上面可以看出,对象冒充继承是在子类Web构造函数里面通过call方法继承父类Worker的构造函数的属性和方法;原型链继承通过子类Web的原型对象等于父类Worker的原型对象来实现继承;最后这两种继承的组合方式实现了完美继承。
4.2 typescript中的类
内容概述: ts中类的定义、继承、类修饰符、静态属性和静态方法、多态、抽象类和抽象方法
4.2.1 ts中类的定义
ts中类的定义和es6类的定义一样
class PersonDefine { name: string // 属性,前面省略了public关键词 constructor(name:string) { //构造函数 this.name = name } run():string { // 原型 return ` ${ this.name}在运动` } } var define = new PersonDefine( '类的定义') alert(define.run())
4.2.2 继承
ts中继承比es5简单很多,用extends super实现继承class WebExtend extends PersonDefine { constructor(name:string) { super(name) // super继承父类的构造函数,并向父类构造函数传参 } work():string { return ` ${ this.name}在工作` } } var extend = new WebExtend( '继承') alert(extend.run()) alert(extend.work())
4.2.3 ts类里面的修饰符
修饰符:typescript里面定义属性的时候给我们提供了三种修饰符
public: 公有修饰符,在当前类里面、子类、类外面都可以访问 protected:保护类型,在当前类里面、子类里面可以访问,在类外部没法访问 private :私有修饰符,在当前类里面可以访问,子类、类外部都没法访问注意:属性如果不加修饰符,默认就是公有修饰符
// 以private为例 class PersonPrivate{ private name:string; /*被private修饰的属性 => 私有属性*/ constructor(name:string){ this.name=name; } run():string{ return ` ${ this.name}在运动` // 私有属性只能在当前类里面可以访问 } } class Web extends PersonPrivate{ constructor(name:string){ super(name) } work(){ // return `${this.name}在工作` // 报错,子类不能访问父类的私有属性 } } var privateName = new PersonPrivate( 'private') alert(privateName.run()) // console.log(privateName.name) // 报错,外部不能访问类的私有属性
4.2.4 静态属性和静态方法
为什么要用静态属性和静态方法?jq里面的$.ajax就是用的静态方法function $( element) { return new Base(element) } function Base( element) { this.element = document.getElementById(element) this.css = function( arr, value) { this.element.style[arr] = value } } $( 'box').css( 'color', 'red') $.ajax = function( ) {} // 想要在$上使用方法怎么办,用静态方法ts中实现静态属性和静态方法用static
class PersonStatic{ /*公有属性*/ public name:string; constructor(name:string) { this.name=name; } /*实例方法(需要被实例化,所以为实例方法)*/ run(){ return ` ${ this.name}在运动` } /*静态属性*/ static sex = '男' /*静态方法,里面没法直接调用类里面的属性*/ static info(){ // return 'info方法' + this.name // 静态方法不能调用本类的方法和属性,可以调用静态属性 return 'info方法' + PersonStatic.sex } } console.log( '静态方法' + PersonStatic.info()) console.log( '静态属性' + PersonStatic.sex)
4.2.5 多态
父类定义一个方法不去实现,让继承它的子类去实现,每一个子类的该方法有不同的表现 多态属于继承比如定义一个父类Animal,里面的eat方法不去实现,让子类Dog和Cat分别实现自己的eat方法
class Animal { name:string; constructor(name:string) { this.name=name; } eat(){ // eat方法继承它的子类去实现 } } class Dog extends Animal{ constructor(name:string){ super(name) } eat(){ return this.name+ '吃粮食' } } class Cat extends Animal{ constructor(name:string){ super(name) } eat(){ return this.name+ '吃老鼠' } }
4.2.6 抽象类和抽象方法
定义:用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(抽象类的子类)中实现 抽象类:它是提供其他类继承的基类,不能直接被实例化,子类继承可以被实例化 abstract修饰的方法(抽象方法)只能放在抽象类里面 抽象类和抽象方法用来定义标准(比如定义标准为:抽象类Animal有抽象方法eat,要求它的子类必须包含eat方法)abstract class AnimalAbst{ public name:string; constructor(name:string){ this.name=name; } abstract eat():any; //抽象方法不包含具体实现并且必须在派生类中实现 run(){ console.log( '其他方法可以不实现') } } // var a = new Animal() /*错误的写法,抽象类不能被实例化*/ class DogAbst extends Animal{ //抽象类的子类必须实现抽象类里面的抽象方法 constructor(name:any){ super(name) } eat(){ return this.name + '吃粮食' } } var d = new DogAbst( '小花花'); console.log( '抽象类和抽象方法',d.eat());
五、TypesSript接口
接口定义 :接口是对传入参数进行约束;或者对类里面的属性和方法进行声明和约束,实现这个接口的类必须实现该接口里面属性和方法;typescript中的接口用interface关键字定义。
接口作用:接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
内容概述:接口分类:(属性接口、函数类型接口、可索引接口、类类型接口),接口的继承
5.1 接口分类
5.1.1 属性接口
对传入对象的约束(也就是对json的约束)在了解接口之前,我们来看看函数传入obj参数
function printLabel( labelInfo: {label:string}){ return labelInfo } // printLabel({name:'obj'}); //错误的写法 console.log(printLabel({ label: 'obj'}))
和上面类似,由此引入属性接口 => 对方法传入参数进行约束
下面为属性接口的例子,方法printFullName对传入参数FullName(为对象)进行约束
interface FullName{ firstName: string; // 注意;结束 secondName: string; age?: number // 接口的可选属性用? } function printFullName( name:FullName) { // 传入对象必须包含firstName和secondName,可传可不传age return name } var obj = { firstName: '小', secondName: '明', age: 20 } console.log(printFullName(obj))
属性接口应用:原生js封装ajax
interface Config{ type: string; url: string; data?: string; dataType: string; } function ajax( config: Config) { var xhr = new XMLHttpRequest xhr.open(config.type, config.url, true) xhr.send(config.data) xhr.onreadystatechange = function( ) { if(xhr.readyState == 4 && xhr.status == 200) { if(config.dataType == 'json'){ console.log( JSON.parse(xhr.responseText)) } else{ console.log(xhr.responseText) } } } } ajax({ type: 'get', data: 'name=xiaoming', url: 'http://a.itying.com/api/productlist', dataType: 'json' })
5.1.2 函数类型接口
对方法传入的参数以及返回值进行约束interface encrypt{ (key: string, value: string): string; // 传入的参数和返回值的类型 } var md5:encrypt = function( key:string, value:string): string{ // encrypt对加密方法md5进行约束,同时md5方法的参数和返回值类型和encrypt要保持一致 return key + value } console.log(md5( 'name', '小明'))
5.1.3 可索引接口
对索引和传入参数的约束(一般用于对数组、对象的约束)ts中定义数组:
var arr1:number[] = [ 1, 2] var arr2: Array<string> = [ '1', '2']
现在用接口来实现:
// 对数组的的约束 interface UserArr{ // 索引为number,参数为string [index:number]: string } var userarr:UserArr = [ 'a', 'b'] console.log(userarr)
// 对象的约束 interface UserObj{ // 索引为string,参数为string [index:string]: string } var userobj:UserObj = { name: '小明', sex: '男' } console.log(userobj)
5.1.4 类类型接口
对类的约束,和抽象类抽象有点相似interface Animal{ // 对类里面的属性和方法进行约束 name:string; eat(str:string): void; } // 类实现接口要用implements关键字,必须实现接口里面声明的方法和属性 class Cat implements Animal{ name:string; constructor(name:string){ this.name = name } eat(food:string){ console.log( this.name + '吃' + food) } } var cat = new Cat( '小花') cat.eat( '老鼠')
5.2 接口的继承
和类的继承一样,用extends实现接口继承下面同时实现类的继承和接口的继承
interface Animal { eat(): void; } // 继承Animal接口,则实现Person接口的类必须也实现Animal接口里面的方法 interface Person extends Animal { work(): void; } class Programmer { public name: string; constructor(name: string) { this.name = name; } coding(code: string) { console.log( this.name + code) } } // 继承类并且实现接口 class Web extends Programmer implements Person { constructor(name: string) { super(name) } eat() { console.log( this.name + '吃') } work() { console.log( this.name + '工作'); } } var w = new Web( '小李'); w.eat(); w.coding( '写ts代码');
六、TypesSript泛型
泛型定义 :泛型定义:泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)。ts中用T表示泛型。
泛型公式: <T>表示泛型,调用的时候指定T的数据类型软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
内容概述:内容概述:函数的泛型、类的泛型、泛型接口
6.1 函数的泛型
传入的参数类型和返回的参数类型可以指定我们来看看函数用ts数据类型,想要同时返回string类型和number类型
function getData1( value:string): string{ return value; } function getData2( value:number): number{ return value; }
这样要写不同的函数,不能按照需求返回不同类型数据,造成代码冗余 => 由此引入泛型
<T>表示泛型,调用的时候指定T的数据类型function dataT< T>( value:T): T{ // 传入参数为T 返回值为T return value } dataT<number>( 1) // 调用指定泛型为number类型,则传入参数也必须为number类型 dataT<string>( 'string') function dataAny< T>( value:T): any{ return '传入参数为T,任意类型返回值'; } dataAny<number>( 123); // 参数必须是number dataAny<string>( '这是一个泛型');
6.2 类的泛型
也是用<T>来实现类的泛型,new的时候指定T的数据类型有个最小堆算法,需要同时支持返回数字和字符串两种类型
使用泛型之前:只能在类的类部指定数据类型,实现需求还要写一套string类型的类
class MinClass{ public list:number[]=[]; add(num:number){ this.list.push(num) } min():number{ var minNum= this.list[ 0]; for( var i= 0;i< this.list.length;i++){ if(minNum> this.list[i]){ minNum= this.list[i]; } } return minNum; } } var m= new MinClass(); m.add( 1); m.add( 2); alert(m.min());
使用泛型之后:只用一套类来实现
class MinClassT< T>{ public list:T[]=[]; add(value:T): void{ this.list.push(value); } min():T{ var minNum= this.list[ 0]; for( var i= 0;i< this.list.length;i++){ if(minNum> this.list[i]){ minNum= this.list[i]; } } return minNum; } } var m1= new MinClassT<number>(); /*实例化类 并且指定了类的T代表的类型是number*/ m.add( 1); m.add( 2); alert(m1.min()) var m2= new MinClassT<string>(); /*实例化类 并且指定了类的T代表的类型是string*/ m2.add( 'c'); m2.add( 'a'); alert(m2.min())
6.3 泛型接口
有一个函数类型接口
interface ConfigFn{ (value:string):string; } var setData:ConfigFn = function( value:string): string{ return value } setData( 'name'); // setData(20); // 错误
setData(20);写法错误,想要传入number类型的参数又要写一个函数类型接口 => 用泛型接口
泛型接口有两种写法:
// 泛型接口定义方式一 interface ConfigFnOne{ <T>(value:T):T; } var setDataOne:ConfigFnOne = function< T>( value:T): T{ return value } // 既可以传入string也可以传入number类型参数 setDataOne<string>( 'name'); setDataOne<number>( 20);
// 泛型接口定义方式二 interface ConfigFnTwo<T>{ (value:T):T; } function setDataTwo< T>( value:T): T{ return value } var setDataTwoFn:ConfigFnTwo<string> = setDataTwo setDataTwoFn( 'name');
示例代码请查看github,欢迎start https://github.com/dzfrontend...