[TOC]
环境配置和搭建 什么是TypeScript
TypeScript
是JavaScript
的超集,遵循最新的ES5/ES6
规范。TypeScript
扩展了JavaScript
语法
为什么要有TS
TS
更像后端语言,如JAVA
,可以开发大型企业应用
TS
提供的类型系统,可以帮助我们在写代码时提供丰富的语法提示
在编写代码时,会对代码进行类型检查从而避免很多线上错误
TypeScript
不会取代js
,两者是相互共存的
尤雨溪:我认为将类型添加到js
本身是一个漫长的过程。让委员会设计一个类型系统是(根据TC39
的经历来判断)不切实际的
我也是这么认为的
环境配置 全局编译TS
文件 全局安装typescript
对ts
进行编译
1 2 npm i typescript -g tsc --init
本地安装
1 2 npm i typescript -D npx tsc --init
创建tsconfig.json
,以下是默认生成的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 npx tsc --init Created a new tsconfig.json with: TS target: es2016 module: commonjs strict: true esModuleInterop: true skipLibCheck: true forceConsistentCasingInFileNames: true You can learn more at https://aka.ms/tsconfig
新建src/1.ts
1 let string :String = 'hello'
会根据配置文件,将其转为js
语法
使用tsc
指令,将文件转化为js
语法,默认不添加文件名,会将当前目录及子目录下,所有的ts
文件全部转为js
文件,如果是局部安装,则使用npx tsc
指令
1.js
1 2 3 "use strict" ;let string = 'hello' ;
但我们希望的是实时编译,使用npx tsc --watch
指令
1 2 3 4 5 D:\workspace\github\code\project-workshop\code-prac\typescript>npx tsc --watch [下午10:44:16] File change detected. Starting incremental compilation... [下午10:44:16] Found 0 errors. Watching for file changes.
修改1.ts
,是会实时编译的
1 2 let string :String = 'hello1' let hello :String = 'hello'
1.js
1 2 3 4 "use strict" ;let string = 'hello1' ;let hello = 'hello' ;
code runner + ts-node
现在我们还希望,直接运行ts
文件,比如打印下string
变量,目前可行的办法是,编译成js
后,运行js
文件
但很麻烦,借助于code runner
插件(需要在vscode
插件市场搜索安装),并安装npm install ts-node -g
,必须装在全局
1.ts
1 2 3 4 let string :String = 'hello1' let hello :String = 'hello' console .log (hello)
然后选择1.ts
中的代码,右键运行Run Code
,控制台会打印输出hello
同级目录下,会生成tempCodeRunnerFile.js
,内容是选中的内容,然后借助了ts-node
环境执行了该文件
无论是全局编译还是使用code runner + ts-code
来处理ts
文件,在我们真正写代码时,还是不能满足需求的,所以要借助构建工具
可以使用webpack
或rollup
构建工具来处理ts
由于学习ts
的过程中,并不会处理图片等资源,这里的构建工具暂不选取webpack
,而使用rollup
拓展:解析ts
的方式有两种
在rollup
中,会采用rollup-plugin-typescript2
包,配合当前配置文件来解析ts
在webpack
中,会采用ts-loader
或者babel-plugin-typescript
包来解析
rollup
来处理ts
新建rollup.config.js
本地安装
rollup
typescript
rollup-plugin-typescript2
:连接rollup
和typescript
的桥梁
@rollup/plugin-node-resolve
:以node
的方式导入第三方包
rollup-plugin-serve
:起服务相关
在根路径下,npm init -y
初始化package.json
根目录下安装
1 npm i rollup typescript rollup-plugin-typescript2 @rollup/plugin-node-resolve rollup-plugin-serve -D
安装后的package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 { "name" : "typescript" , "version" : "1.0.0" , "description" : "" , "main" : "index.js" , "scripts" : { "test" : "echo \"Error: no test specified\" && exit 1" } , "keywords" : [ ] , "author" : "" , "license" : "ISC" , "devDependencies" : { "@rollup/plugin-node-resolve" : "^13.3.0" , "rollup" : "^2.77.0" , "rollup-plugin-serve" : "^2.0.0" , "rollup-plugin-typescript2" : "^0.32.1" , "typescript" : "^4.7.4" } }
新建src/index.ts
文件
配置rollup.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import {nodeResolve} from "@rollup/plugin-node-resolve" import ts from "rollup-plugin-typescript2" import serve from "rollup-plugin-serve" import path from "path" export default { input :'src/index.ts' , output : { file : path.resolve (__dirname, 'dist/bundle.js' ), format : 'iife' , sourcemap : true }, plugins : [ nodeResolve ({ extensions : ['.js' , '.ts' ] }), ts ({ tsconfig : path.resolve (__dirname, 'tsconfig.json' ) }), serve ({ port : 3000 , contentBase : '' , openPage : '/public/index.html' }) ] }
备注:ts
的配置文件也得配sourceMap
,搜索sourceMap
,取消注释即可
package.json
中新增script
1 2 3 4 "scripts" : { "test" : "echo \"Error: no test specified\" && exit 1" , "dev" : "rollup -cw" } ,
执行npm run dev
报错:
1 2 3 [!] Error: Incompatible tsconfig option. Module resolves to 'CommonJS' . This is incompatible with rollup, please use 'module: "ES2015"' or 'module: "ESNext"' . Error: Incompatible tsconfig option. Module resolves to 'CommonJS' . This is incompatible with rollup, please use 'module: "ES2015"' or 'module: "ESNext"' .
将ts
的配置文件的module
字段的值,改为ESNext
(默认的是commonjs
),target
字段由es2016
改为es5
再次执行npm run dev
成功:
1 2 3 4 5 6 7 8 9 bundles src/index.ts → dist/bundle.js... rpt2: options error TS6053: File 'D:/workspace/github/code/project-workshop/code-prac/src/index.ts' not found. The file is in the program because: Root file specified for compilation http://localhost:3000 -> D:\workspace\github\code\project-workshop\code-prac\typescript (!) Generated an empty chunk index created dist/bundle.js in 4.5s
打开localhost:8080/public/index.html
,看下控制台效果
注意index.html
中,引入bundle.js
的路径,rollup
会自己去找
1 2 3 <body > <script src ="/dist/bundle.js" > </script > </body >
当然也可以直接使用相对路径(推荐)
1 2 3 <body > <script src ="../dist/bundle.js" > </script > </body >
目前我们是没有写任何ts
代码的,所以会有如上提示
在src/index.ts
中写点内容
1 2 3 let str :String = "hello" console .log (str)
注意要打印该变量(使用该变量),因为rollup
默认开启了tree-shaking
保存后查看dist/bundle.js
1 2 3 4 5 6 7 8 9 (function ( ) { 'use strict' ; var str = "hello" ; console .log (str); })();
基本类型
最基本的类型有,数字、字符、布尔
所有的类型都在冒号后面,ts
的核心:一切都以安全为准
什么时候可以不用类型(推导)
基本演示
1 2 3 4 5 let num :number = 1 let str :string = 'sai' let bool :boolean = true
数字类型 1 2 3 4 5 6 7 let num2 :Number = 2 let num3 :number = Number (1 )let num4 :Number = new Number (1 )console .log (num2, num3, num4)
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 (function ( ) { 'use strict' ; var num2 = 2 ; var num3 = Number (1 ); var num4 = new Number (1 ); console .log (num2, num3, num4); })();
数组类型 1 2 3 4 5 6 const arr :number [] = [] const arr2 :(number | string )[] = ['a' , 1 ] const arr3 :any [] = ['' , 1 , {}] const arr4 :Array <boolean > = [true , false ]
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 (function ( ) { 'use strict' ; var arr = []; var arr2 = ['a' , 1 ]; var arr3 = ['' , 1 , {}]; var arr4 = [true , false ]; console .log (arr, arr2, arr3, arr4); })();
元组类型 1 2 3 4 5 6 7 8 9 const tuple :[string , boolean , number ] = ['a' , true , 2 ] let r = tuple.pop () console .log (r)tuple.push ('bb' , 1 )
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 (function ( ) { 'use strict' ; var tuple = ['a' , true , 2 ]; var r = tuple.pop (); console .log (r); tuple.push ('bb' , 1 ); })();
枚举类型 ts
最终编译成的js
是没有类型的,只是在开发时使用,分为普通枚举、异构枚举和常量枚举
null
和undefined
null
和undefined
是任何类型的子类型,可以赋值给其他类型
1 let name :number = undefined
修改ts
的配置文件,取消strictNullChecks
字段的注释,设置为false
,或者不用改这里,直接取消严格模式
由于name
变量已经在ts
的全局环境中定义了,所以导出当前的变量
1 2 3 let name :number = undefined export {}
bundle.js
1 2 3 4 5 6 7 8 9 (function ( ) { 'use strict' ; var name = undefined ; console .log (name); })();
我们恢复对ts
配置文件的修改,在严格模式下,undefined
赋值给undefined
,null
赋值给null
,是没有问题的
1 2 3 let u :undefined = undefined let n :null = null console .log (u, n)
bundle.js
1 2 3 4 5 6 7 8 9 10 (function ( ) { 'use strict' ; var u = undefined ; var n = null ; console .log (u, n); })();
never
表示代码无法达到终点(无法执行到),也是任何类型的子类型
出错
1 2 3 4 5 6 function throwError ( ):never { throw new Error () } let xxx :string = throwError ()
死循环
1 2 3 4 5 function whileTrue ( ):never { while (true ) { } }
永远走不到的判断
做代码的完整性校验
1 2 3 4 5 6 7 8 9 function setVal (val:string ) { if (typeof val === 'string' ) { } else { val } }
void
void
一般用来描述函数返回值,也可以描述变量
只能把undefined
和null
赋值给void
1 2 3 4 5 function getVoid ( ):void { } a = getVoid ()
object
非原始数据,后面泛型约束,会大量使用object
1 2 3 4 5 6 7 8 9 10 function create (obj:object ) {} create ({})create (function ( ) {}) create ([])
symbol
和bigint
js
本身就有的类型,并不是ts
提供的
Symbol
1 2 3 let s1 :symbol = Symbol (1 )let s2 :symbol = Symbol (2 )console .log (s1 === s2)
无法编译成es5
,会原样输出
bundle.js
1 2 3 4 5 6 7 8 9 10 (function ( ) { 'use strict' ; var s1 = Symbol (1 ); var s2 = Symbol (2 ); console .log (s1 === s2); })();
bigint
1 2 3 4 let max = Number .MAX_SAFE_INTEGER console .log (max + 1 === max + 2 ); let r1 :bigint = BigInt (max)console .log (BigInt (max) + BigInt (1 ) === BigInt (max) + BigInt (2 ))
bundle.js
1 2 3 4 5 6 7 8 9 10 11 (function ( ) { 'use strict' ; var max = Number .MAX_SAFE_INTEGER ; console .log (max + 1 === max + 2 ); BigInt (max); console .log (BigInt (max) + BigInt (1 ) === BigInt (max) + BigInt (2 )); })();
联合类型 联合类型如果不进行赋值,在没有确定类型之前,默认会取公共方法
1 let numOrStr :string | number
在确定类型后,可以设置相对应的方法
赋予类型后,可以根据上下文自动推断对应类型的方法
场景:
在取值的时候,也会遇到联合类型
1 2 const ele :HTMLElement | null = document .getElementById ('app' ) ele.innerHTML = 'abc'
直接就提示了ele
可能为空,备注:HTMLElement
类型是ts
内部提供的类型
之前我们可以通过
1 2 3 if (ele) { ele.innerHTML = 'abc' }
或者
1 ele && (ele.innerHTML = 'abc' )
来进行判断
但现在我们就明确知道了,ele
一定不为空,可以使用非空断言:!
,告诉ts
,这个东西一定有值,后续出事我负责
1 2 3 4 5 ele!.innerHTML = 'abc' let a :string | number | undefined a!.toString ()
上面是去除掉了空的情况,也可以直接使用as
或<>
强转某个类型
强制告诉ts
,当前这个变量,就是其中某个类型的一个
强转要求必须在联合类型中用才行
1 2 3 let a :string | number | undefined (<string >a).indexOf ('abc' )
为了避免和jsx
语法中的尖括号冲突,强转的语法可以用as
来描述,这也是一种断言
1 2 3 let a :string | number | undefined (a as string ).indexOf ('abc' )
可以使用双重断言,将a
强转成boolean
类型,但不建议这么搞
1 2 3 let a :string | number | undefined (a as any) as boolean
双重断言:先转换成any
,再转换成一个具体的类型。
问题:会导致类型出问题
拓展 ?
还有一个符号是?
,表示aa && aa.xxx
,这个是js
中本来就有的
1 2 3 const ele :HTMLElement | null = document .getElementById ('app' ) ele?.style ele?.style ?.color
bundle.js
1 2 3 4 5 6 7 8 9 10 11 (function ( ) { 'use strict' ; var _a; var ele = document .getElementById ('app' ); ele === null || ele === void 0 ? void 0 : ele.style ; (_a = ele === null || ele === void 0 ? void 0 : ele.style ) === null || _a === void 0 ? void 0 : _a.color ; })();
样例二
1 2 3 4 5 6 7 var aa = { bb : { cc : '123' } } console .log (aa?.bb ?.cc )
??
false ?? true
的结果是多少呢?
只要第一个的值不是null
或者undefined
,就将第一个的值返回,否则就返回第二个值(表示排除null
和undefined
)
还有的操作符有:||
、&&
、|
、&
字面量类型 类型的内容是固定的,已经确定好了,变量的值只能是这几个
1 let type : 'a' | 'b' | 'c' | 'd' = 'b'
但是这样写,会导致类型过于复杂(太长了),我们可以把类型单独提出来
使用ts
中的类型别名
1 2 3 type IType = 'a' | 'b' | 'c' | 'd' let type :IType = 'b' let type2 :IType = 'c'
值虽然可以写对象,但一般不这么搞
应用场景:接口的参数、状态码、下拉框的值
函数 类型 可以对函数增加类型
对函数的参数进行类型校验
对函数的返回值进行类型校验
也可以对函数本身进行校验(声明、参数、返回值)
有两种声明函数的方式
函数关键字function name(){}
入参默认类型为any
,返回值默认类型为void
可以指定入参和返回值类型
1 2 3 function sum (x:string , y:string ):string { return x + y }
let myFuc = function() {}
,可以自动根据当前等号右边的内容,推断左边的内容
1 2 3 let res = (x :number , y :number ):number => { return x + y }
res
的类型是(x:number, y:number) => number
,可以显示指定
1 2 3 let res :(x:number , y:number ) => number = (x :number , y :number ):number => { return x + y }
但这样写比较恶心,优化一下写法
1 2 3 4 type IFn = (x: number , y: number ) => number let res : IFn = (x : number , y : number ): number => { return x + y }
如果此时这么写了,后面函数就不需要再指定参数和返回值类型了,会自动匹配你写的函数,是否符合前面指定的要求
1 2 3 4 5 type IFn = (x: number , y: number ) => number let res : IFn = (x, y ) => { return x + y } res ('a' , 2 )
如果入参中类型不匹配,会给出提示
小结:
自动根据等号右边的内容,推断左边的类型(比较常用)
可以指定类型,右边赋予一个可以兼容这个类型的函数
参数 参数可以设置不传,使用?
(可选参数)对参数进行限制
1 2 3 let res = (x :number , y?:number ):number => { return x + y }
但是直接这样写,return
的时候可能会有问题,因为y
通过?
进行限制,表示可传可不传
那怎么处理呢?给一个默认值嘛
js
中默认值和可选参数不能一起使用
使用明确指出y
一定不为空
1 2 3 let res = (x :number , y?:number ):number => { return x + y! }
或者使用as
(一般我们都是用as
)
1 2 3 let res = (x :number, y?:number):number => { return x + (y as number) }
那么剩余运算符,在ts
中怎么用呢?
但是要指出类型
1 2 3 4 let res = (x :number , y?:number , ...args :number []):number => { return x + (y as number ) } res (1 , 2 , 3 , 4 , 5 )
重载 一个方法,根据参数的不同,实现不同的功能,ts
的目的就是根据不同的参数返回类型
需求:将数值123
转为[1, 2, 3]
,并将字符串abc
转为['a', 'b', 'c']
1 2 3 4 5 6 7 8 9 10 function toArray (value: number | string ): number [] | string [] { if (typeof value == 'string' ) { return value.split ('' ) } else { return value.toString ().split ('' ).map (item => Number (item)) } } const res = toArray ('abc' )console .log (res)
这样写有个问题,传的是number,但是返回的可能是string数组(反之),应该限制一下(但事实上对于打包后的js
来说,经过测试后,好像是没问题的)
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 (function ( ) { 'use strict' ; function toArray (value ) { if (typeof value == 'string' ) { return value.split ('' ); } else { return value.toString ().split ('' ).map (function (item ) { return Number (item); }); } } var res = toArray ('abc' ); console .log (res); })();
这时就要用到函数的重载
重载方法需要写法函数的上面
1 2 3 4 5 6 7 8 9 10 11 12 function toArray (value:number ):number [] function toArray (value:string ):string []function toArray (value: number | string ): number [] | string [] { if (typeof value == 'string' ) { return value.split ('' ) } else { return value.toString ().split ('' ).map (item => Number (item)) } } const res = toArray ('abc' )console .log (res)
但打包后的js
还是原来的
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 (function ( ) { 'use strict' ; function toArray (value ) { if (typeof value == 'string' ) { return value.split ('' ); } else { return value.toString ().split ('' ).map (function (item ) { return Number (item); }); } } var res = toArray ('abc' ); console .log (res); })();
写了老半天,写了一堆空气,○( ^皿^)っHiahiahia…
ts
的目的:为了安全,以及更好的提示
类 类,最早都是用构造函数来替代的,如果es6
中的类,编译成es5
,最终还是会编译成函数的
类有哪些特点呢?
实例属性、实例方法、静态属性、静态方法、原型属性、原型方法
实例属性、方法
这样就声明了一个类,一般都大写
类里面有一个构造函数,当使用new
操作符,new Pointer
时,执行的就是这个构造函数,在构造函数中的操作,都是初始化操作
对于ts
而言,在使用必须先声明类型,声明的变量会被增加到实例上,可以通过实例来调用该属性
1 2 3 4 5 6 7 8 9 10 11 12 class Pointer { x : number y : number constructor (x: number , y: number ) { this .x = x this .y = y } } new Pointer (100 , 200 )
如果在构造函数中没有进行初始化,则需要在声明时进行初始化,否则会报错
1 2 3 4 5 6 7 8 9 10 class Pointer { x : number y : number constructor (x: number , y: number ) { } } new Pointer (100 , 200 )
报错信息:提示参数没有初始化
在声明时进行初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 class Pointer { x : number = 1 y : number = 2 constructor (x: number , y: number ) { } } const res = new Pointer (100 , 200 )console .log (res, res.x )
看下bundle.js
,这里是转成了es5
了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 (function ( ) { 'use strict' ; var Pointer = (function ( ) { function Pointer (x, y ) { this .x = 1 ; this .y = 2 ; } return Pointer ; }()); var res = new Pointer (100 , 200 ); console .log (res, res.x ); })();
在声明类型的同时初始化,就相当于在实例的构造函数中初始化了
继续上面的:
但是呢,我们一般只会在构造函数里面赋值
另外,如果我们就不想初始化,可以使用非空断言,或者取消配置文件的严格模式(不推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 class Pointer { x!: number y!: number constructor (x: number , y: number ) { } } const res = new Pointer (100 , 200 )console .log (res, res.x )
但是一般情况下,我们还是会在构造函数中赋值的
1 2 3 4 5 6 7 8 9 10 11 12 class Pointer { x : number y : number constructor (x: number , y: number ) { this .x = x this .y = y } } const res = new Pointer (100 , 200 )console .log (res, res.x )
构造函数它也是个函数,里面依然可以使用剩余运算符、可选参数、默认参数
属性修饰符 public
对于类型声明,默认是由public
关键字修饰的
上面的等价于
1 2 3 4 5 6 7 8 9 10 11 class Pointer { public x : number public y : number constructor (x: number , y: number ) { this .x = x this .y = y } } const res = new Pointer (100 , 200 )console .log (res, res.x )
当然也可以将传入的参数,直接放在实例上,无需再次声明。public
是属性修饰符,js
中是没有的
等价写法如下:
1 2 3 4 5 6 7 8 9 class Pointer { constructor (public x: number , public y: number ) { this .x = x this .y = y } } const res = new Pointer (100 , 200 )console .log (res, res.x )
值得注意的是,将传入的参数,直接放在实例上时,相当于已经给实例进行了初始化赋值操作,构造函数中不必再初始化了
只要在构造函数外面,声明了变量,这个变量就会被添加到实例上(等价写法,相当于在构造函数外面声明了变量)
1 2 3 4 5 6 7 8 9 10 11 class Pointer { constructor (public x: number , public y: number ) { } } const res = new Pointer (100 , 200 )console .log (res, res.x )
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 (function ( ) { 'use strict' ; var Pointer = (function ( ) { function Pointer (x, y ) { this .x = x; this .y = y; } return Pointer ; }()); var res = new Pointer (100 , 200 ); console .log (res, res.x ); })();
private
表示只有自己能访问的属性
先看一个简单继承的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Animal { constructor (public name: string , public age: number ) { } } class Cat extends Animal { constructor (name: string , age: number , public address: string ) { super (name, age); } } let cat = new Cat ('Tom' , 8 , 'USA' )console .log (cat) export {}
看下bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 (function ( ) { 'use strict' ; var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; return _this; } return Cat ; }(Animal )); var cat = new Cat ('Tome' , 8 , 'USA' ); console .log (cat); })();
我们在父类和子类中构造函数中,都打印一下name
属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Animal { constructor (public name: string , public age: number ) { console .log (this .name ) } } class Cat extends Animal { constructor (name: string , age: number , public address: string ) { super (name, age); console .log (this .name ) } } let cat = new Cat ('Tom' , 8 , 'USA' )console .log (cat.name ) export {}
如果将父类的构造函数的name
修饰符,改成private
,子类及子类实例就不能访问了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Animal { constructor (private name: string , public age: number ) { console .log (this .name ) } } class Cat extends Animal { constructor (name: string , age: number , public address: string ) { super (name, age); console .log (this .name ) } } let cat = new Cat ('Tom' , 8 , 'USA' )console .log (cat.name )export {}
protected
表示只有自己,和自己的子孙可以访问
外界是访问不了的
将参数修饰符改为protected
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Animal { constructor (protected name: string , public age: number ) { console .log (this .name ) } } class Cat extends Animal { constructor (name: string , age: number , public address: string ) { super (name, age); console .log (this .name ) } } let cat = new Cat ('Tom' , 8 , 'USA' )console .log (cat.name ) export {}
补充
我们可以给构造函数添加修饰符,默认的是public
protected
可以被继承,不能被new
new
是在实例化,调用这个构造函数,而protected
的函数只能在类的内部被调用
private
readonly
表示只读属性,在初始化完毕后,就不能修改了,有点类似于const
值得注意的是,在constructor
中,表示的都是初始化操作,在constructor
里面,是可以修改的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Animal { public readonly n : number = 1 public constructor (public name: string , public age: number ) { console .log (this .name ) this .n = 100 } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); console .log (this .name ) } } let cat = new Cat ('Tom' , 8 , 'USA' )console .log (cat.name )export {}
不能修改的只是变量的引用
上例中,如果address
是个对象,修改的是对象里面的属性,是可以的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Animal { public readonly n : number = 1 public constructor (public name: string , public age: number ) { console .log (this .name ) this .n = 100 } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: any ) { super (name, age); console .log (this .name ) } } let cat = new Cat ('Tom' , 8 , {name :'USA' })cat.address .name = 'shanghai' console .log (cat)export {}
静态属性、方法 之前讲过的实例属性、方法,需要通过new
生成实例对象来调用
静态属性、方法,通过类来调用
静态属性 es6
写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Animal { public constructor (public name: string , public age: number ) { } static get type () { return '哺乳动物' } } console .log (Animal .type )export {}
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 (function ( ) { 'use strict' ; var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Object .defineProperty (Animal , "type" , { get : function ( ) { return '哺乳动物' ; }, enumerable : false , configurable : true }); return Animal ; }()); console .log (Animal .type ); })();
编译后的结果,是通过defineProperty
来实现的
es7
语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' } console .log (Animal .type )export {}
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 (function ( ) { 'use strict' ; var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .type = '哺乳动物' ; return Animal ; }()); console .log (Animal .type ); })();
静态方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } } console .log (Animal .type , Animal .getName ()) export {}
bundles.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 (function ( ) { 'use strict' ; var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .type = '哺乳动物' ; return Animal ; }()); console .log (Animal .type , Animal .getName ()); })();
静态方法可以被继承,可以通过子类来调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } } console .log (Cat .type , Cat .getName ()) export {}
子类也可以拿到父类的实例属性、方法
那么是怎么实现的呢?
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 (function ( ) { 'use strict' ; var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .type = '哺乳动物' ; return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; return _this; } return Cat ; }(Animal )); console .log (Cat .type , Cat .getName ()); })();
看下编译后的结果,看下__extends(Cat, _super)
,它会把子类和父类都传进来,只做了一件事extendStatics
,继承静态属性,把子类的原型对象指向父类的原型对象,这样子类就可以通过原型类查找来调用父类的属性或方法了
如果子类中,有同名方法,调用的就是自己身上的属性和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } static type = '猫科动物' static getName ( ) { return '猫' } } console .log (Cat .type , Cat .getName ()) export {}
此时,如果想要访问父类的同名属性和方法,需要在子类的同名方法中,使用super
关键字,获取到父类,然后调用方法(构造函数和静态方法中的super
,默认指向的都是自己的父类,在原型方法中的super
,指向父类的原型)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } static type = '猫科动物' static getName ( ) { console .log (super .getName ()) return '猫' } } console .log (Cat .type , Cat .getName ()) export {}
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 (function ( ) { 'use strict' ; var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .type = '哺乳动物' ; return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; return _this; } Cat .getName = function ( ) { console .log (_super.getName .call (this )); return '猫' ; }; Cat .type = '猫科动物' ; return Cat ; }(Animal )); console .log (Cat .type , Cat .getName ()); })();
原型属性、方法 原型方法 直接在类中写的方法,就是原型方法
可以通过属性访问器,定义原型属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } static type = '猫科动物' static getName ( ) { console .log (super .getName ()) return '猫' } say ( ) { console .log ('miao' ) } } console .log (Cat .type , Cat .getName ()) export {}
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 (function ( ) { var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .type = '哺乳动物' ; return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; return _this; } Cat .getName = function ( ) { console .log (_super.getName .call (this )); return '猫' ; }; Cat .prototype .say = function ( ) { console .log ('miao' ); }; Cat .type = '猫科动物' ; return Cat ; }(Animal )); console .log (Cat .type , Cat .getName ()); })();
原型属性 原型属性也能不能直接写在类中呢,我们先写一下,看下编译后的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } static type = '猫科动物' static getName ( ) { console .log (super .getName ()) return '猫' } aaaaa = 1 say ( ) { console .log ('miao' ) } } console .log (Cat .type , Cat .getName ()) export {}
aaaaa
被放在了实例上
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 (function ( ) { 'use strict' ; var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .type = '哺乳动物' ; return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; _this.aaaaa = 1 ; return _this; } Cat .getName = function ( ) { console .log (_super.getName .call (this )); return '猫' ; }; Cat .prototype .say = function ( ) { console .log ('miao' ); }; Cat .type = '猫科动物' ; return Cat ; }(Animal )); console .log (Cat .type , Cat .getName ()); })();
那么怎么加原型属性呢?
使用get
修饰符(属性访问器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } static type = '猫科动物' static getName ( ) { console .log (super .getName ()) return '猫' } get content () { return 'aaa' } set content (newVal:string ) { } say ( ) { console .log ('miao' ) } } console .log (Cat .type , Cat .getName ()) export {}
可以看到编译后,给Cat.prototype
定义了属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 (function ( ) { 'use strict' ; var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .type = '哺乳动物' ; return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; return _this; } Cat .getName = function ( ) { console .log (_super.getName .call (this )); return '猫' ; }; Object .defineProperty (Cat .prototype , "content" , { get : function ( ) { return 'aaa' ; }, set : function (newVal ) { }, enumerable : false , configurable : true }); Cat .prototype .say = function ( ) { console .log ('miao' ); }; Cat .type = '猫科动物' ; return Cat ; }(Animal )); console .log (Cat .type , Cat .getName ()); })();
属性访问器的好处:可以访问私有属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } say ( ) { console .log ('父' ) } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } static type = '猫科动物' static getName ( ) { console .log (super .getName ()) return '猫' } private str : string = '' get content () { return this .str } set content (newVal: string ) { this .str = newVal } say ( ) { super .say () } } let cat = new Cat ('Tom' , 8 , 'USA' )cat.content = 'abc' console .log (cat.content ) export {}
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 (function ( ) { 'use strict' ; var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .prototype .say = function ( ) { console .log ('父' ); }; Animal .type = '哺乳动物' ; return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; _this.str = '' ; return _this; } Cat .getName = function ( ) { console .log (_super.getName .call (this )); return '猫' ; }; Object .defineProperty (Cat .prototype , "content" , { get : function ( ) { return this .str ; }, set : function (newVal ) { this .str = newVal; }, enumerable : false , configurable : true }); Cat .prototype .say = function ( ) { _super.prototype .say .call (this ); }; Cat .type = '猫科动物' ; return Cat ; }(Animal )); var cat = new Cat ('Tom' , 8 , 'USA' ); cat.content = 'abc' ; console .log (cat.content ); })();
原型方法中的super
原型方法中的super
,指向的是父类原型
我们在父类中,加上个say
方法,并在子类中调用,最后实例上调用say
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Animal { public constructor (public name: string , public age: number ) { } static type = '哺乳动物' static getName ( ) { return '动物' } say ( ) { console .log ('父' ) } } class Cat extends Animal { public constructor (name: string , age: number , public readonly address: string ) { super (name, age); } static type = '猫科动物' static getName ( ) { console .log (super .getName ()) return '猫' } get content () { return 'aaa' } set content (newVal:string ) { } say ( ) { super .say () } } let cat = new Cat ('Tom' , 8 ,'USA' )cat.say () console .log (cat)export {}
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 (function ( ) { 'use strict' ; var extendStatics = function (d, b ) { extendStatics = Object .setPrototypeOf || ({ __proto__ : [] } instanceof Array && function (d, b ) { d.__proto__ = b; }) || function (d, b ) { for (var p in b) if (Object .prototype .hasOwnProperty .call (b, p)) d[p] = b[p]; }; return extendStatics (d, b); }; function __extends (d, b ) { if (typeof b !== "function" && b !== null ) throw new TypeError ("Class extends value " + String (b) + " is not a constructor or null" ); extendStatics (d, b); function __ ( ) { this .constructor = d; } d.prototype = b === null ? Object .create (b) : (__.prototype = b.prototype , new __ ()); } var Animal = (function ( ) { function Animal (name, age ) { this .name = name; this .age = age; } Animal .getName = function ( ) { return '动物' ; }; Animal .prototype .say = function ( ) { console .log ('父' ); }; Animal .type = '哺乳动物' ; return Animal ; }()); var Cat = (function (_super ) { __extends (Cat , _super); function Cat (name, age, address ) { var _this = _super.call (this , name, age) || this ; _this.address = address; return _this; } Cat .getName = function ( ) { console .log (_super.getName .call (this )); return '猫' ; }; Object .defineProperty (Cat .prototype , "content" , { get : function ( ) { return 'aaa' ; }, set : function (newVal ) { }, enumerable : false , configurable : true }); Cat .prototype .say = function ( ) { _super.prototype .say .call (this ); }; Cat .type = '猫科动物' ; return Cat ; }(Animal )); var cat = new Cat ('Tom' , 8 , 'USA' ); cat.say (); console .log (cat); })();
装饰器 es7
中的
装饰器是一个实验性语法,语法会有改动,vue2
中刚开始用的就是装饰器,要使用这个功能,需要注释掉ts
配置文件中的experimentalDecorator
选项,设置为true
装饰器的作用,就是扩展类中的属性和方法
可以在不同的类上,增加装饰器以增加不同的功能,实现复用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function addSay1 (target:any ) { console .log ('1' ) } function addSay2 (target:any ) { console .log ('2' ) } function addSay3 (target:any ) { console .log ('3' ) } @addSay1 @addSay2 @addSay3 class Person {}
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 (function ( ) { 'use strict' ; var __decorate = (undefined && undefined .__decorate ) || function (decorators, target, key, desc ) { var c = arguments .length , r = c < 3 ? target : desc === null ? desc = Object .getOwnPropertyDescriptor (target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect .decorate === "function" ) r = Reflect .decorate (decorators, target, key, desc); else for (var i = decorators.length - 1 ; i >= 0 ; i--) if (d = decorators[i]) r = (c < 3 ? d (r) : c > 3 ? d (target, key, r) : d (target, key)) || r; return c > 3 && r && Object .defineProperty (target, key, r), r; }; function addSay1 (target ) { console .log ('1' ); } function addSay2 (target ) { console .log ('2' ); } function addSay3 (target ) { console .log ('3' ); } ((function ( ) { function Person ( ) { } Person = __decorate ([ addSay1, addSay2, addSay3 ], Person ); return Person ; })()); })();
等价于addSay1(addSay2(addSay3(Person)))
,结果:
装饰器只能修饰类,不能修饰函数,因为函数会有变量提升的问题
洋葱模型 如果装饰器本身返回的是个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function addSay1 (val: string ) { console .log (val) return function (target: any ) { console .log ('1' ) } } function addSay2 (val: string ) { console .log (val) return function (target: any ) { console .log ('2' ) } } function addSay3 (val: string ) { console .log (val) return function (target: any ) { console .log ('3' ) } } @addSay1 ('a1' )@addSay2 ('a2' )@addSay3 ('a3' ) class Person {}
结果:a1 a2 a3 3 2 1
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 (function ( ) { 'use strict' ; var __decorate = (undefined && undefined .__decorate ) || function (decorators, target, key, desc ) { var c = arguments .length , r = c < 3 ? target : desc === null ? desc = Object .getOwnPropertyDescriptor (target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect .decorate === "function" ) r = Reflect .decorate (decorators, target, key, desc); else for (var i = decorators.length - 1 ; i >= 0 ; i--) if (d = decorators[i]) r = (c < 3 ? d (r) : c > 3 ? d (target, key, r) : d (target, key)) || r; return c > 3 && r && Object .defineProperty (target, key, r), r; }; function addSay1 (val ) { console .log (val); return function (target ) { console .log ('1' ); }; } function addSay2 (val ) { console .log (val); return function (target ) { console .log ('2' ); }; } function addSay3 (val ) { console .log (val); return function (target ) { console .log ('3' ); }; } ((function ( ) { function Person ( ) { } Person = __decorate ([ addSay1 ('a1' ), addSay2 ('a2' ), addSay3 ('a3' ) ], Person ); return Person ; })()); })();
案例 修饰方法 使用装饰器,给person
新增eat
方法
target
参数指向的是类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function eat (target: any ) { target.prototype .eat = function ( ) { console .log ('eat' ) } } @eat class Person { eat!: () => void } let p = new Person ()p.eat ()
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (function ( ) { 'use strict' ; var __decorate = (undefined && undefined .__decorate ) || function (decorators, target, key, desc ) { var c = arguments .length , r = c < 3 ? target : desc === null ? desc = Object .getOwnPropertyDescriptor (target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect .decorate === "function" ) r = Reflect .decorate (decorators, target, key, desc); else for (var i = decorators.length - 1 ; i >= 0 ; i--) if (d = decorators[i]) r = (c < 3 ? d (r) : c > 3 ? d (target, key, r) : d (target, key)) || r; return c > 3 && r && Object .defineProperty (target, key, r), r; }; function eat (target ) { target.prototype .eat = function ( ) { console .log ('eat' ); }; } var Person = (function ( ) { function Person ( ) { } Person = __decorate ([ eat ], Person ); return Person ; }()); var p = new Person (); p.eat (); })();
修饰属性 target
参数指向的是原型(可以看编译后的代码)
1 2 3 4 5 6 7 8 9 10 11 12 function toUpperCase (target:any , key:string ) { } class Person { @toUpperCase public name : string = 'sai' } let p = new Person ()console .log (p.name )
bundle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 (function ( ) { 'use strict' ; var __decorate = (undefined && undefined .__decorate ) || function (decorators, target, key, desc ) { var c = arguments .length , r = c < 3 ? target : desc === null ? desc = Object .getOwnPropertyDescriptor (target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect .decorate === "function" ) r = Reflect .decorate (decorators, target, key, desc); else for (var i = decorators.length - 1 ; i >= 0 ; i--) if (d = decorators[i]) r = (c < 3 ? d (r) : c > 3 ? d (target, key, r) : d (target, key)) || r; return c > 3 && r && Object .defineProperty (target, key, r), r; }; function toUpperCase (target, key ) { } var Person = (function ( ) { function Person ( ) { this .name = 'sai' ; } __decorate ([ toUpperCase ], Person .prototype , "name" , void 0 ); return Person ; }()); var p = new Person (); console .log (p.name ); })();
现在toUpperCase
的功能,是想实现大写,该怎么玩
我们知道代码是顺序执行的,执行到toUpperCase
的时候,name
还没有赋值
装饰器中,使用Object.defineProperty
给target
(原型)增加get
和set
,这样在后面修改值的时候,就可以触发更新了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function toUpperCase (target: any , key: string ) { let val : string = '' Object .defineProperty (target, key, { get ( ) { return val.toUpperCase () }, set (newVal: string ) { console .log (newVal) val = newVal } }) } class Person { @toUpperCase public name : string = 'sai' } let p = new Person ()console .log (p.name )
修饰静态属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 function toUpperCase (target: any , key: string ) { let val : string = '' Object .defineProperty (target, key, { get ( ) { return val.toUpperCase () }, set (newVal: string ) { val = newVal } }) } function double (num:number ) { return function (target:any , key:string ) { let val :number = target[key] Object .defineProperty (target, key, { get ( ) { return val * 2 } }) } } class Person { @toUpperCase public name : string = 'sai' @double (2 ) static age :number = 18 } let p = new Person ()console .log (p.name , Person .age )
修饰静态方法