1.TypeScript 介绍 、自动编译
1.1 介绍
- TypeScript 是由 微软 开发的开源语言
- TypeScript 是 Javascript 的超集,遵循最新的 ES6、ES5 规范,扩展 JavaScript 语法
- TypeScript 更像后端 java、C#这样 面向对象 语言,可以让 js 开发大型企业项目
- 谷歌也支持 Typescript ,它的 Angular2.x+ 就是基于 Typescript 语法
- 最新的 Vue ,React ,Nodejs 框架 Nestjs、midway 中,用的是 TypeScript 语法
1.2 自动编译
- 生成 tsconfig.json:tsc --init
- 修改输出目录:"outDir": "./js"
终端 - 运行任务 - 监视 tsconfig.json
2.TypeScript 数据类型
在 TypeScript 中,写代码必须指定数据类型,如下所示:
① 布尔类型(boolean);
let flag: boolean = true;
flag = 123; // 错误
flag = false; // 正确
② 数字类型(number);
let num: number = 123;
num = 'str'; // 错误
num = 456; // 正确
③ 字符串类型 (string);
let str: string = 'this is ts';
str = 'haha'; // 正确
str = true; // 错误
④ 数组类型(array);
// 第一种方式
let arr: number[] = [11, 22, 33];
// 第二种方式【不推荐前端这么写,一般后端习惯这么定义,写法类似于泛型】
let arr: Array<number> = [11, 22, 33];
⑤ 元组类型(tuple):数组的一种,指定一个数组中 不同类型 的元素;
let arr: [number, string] = [123, 'this is ts'];
⑥ 枚举类型(enum):处理非数值数据;
// 第一种情况:标识符有赋值
enum Flag {
'success' = 1,
'error' = 2,
};
let aaa: Flag = Flag.success; // 1
// 第二种情况:标识符没有赋值
enum Color {
blue,
red,
'orange',
};
let bbb: Color = Color.red; // 输出1,如果 标识符 没有赋值,它的值就是数组下标
// 第三种情况:有的标识符有赋值,有的标识符没有赋值
enum Color {
blue,
red = 3,
'orange',
};
const ccc: Color = Color.orange; // 输出4,前面的标识符赋值了,所以后面比前面的 + 1
⑦ 任意类型(any):重新赋值为不同类型的值不会报错,可以用于获取 DOM 元素类型;
let num: any = 123;
// 赋值为不同类型不会报错
num = 'str';
// 获取 DOM 元素时,如果不定义 DOM 类型,可以使用 any 防止报错
let oBox: any = document.getElementById('box');
oBox.style.color = 'red';
⑧ null 、undefined:其他( 除了 never 类型 )数据类型的子类型;
// 一个元素可能是 number 类型 可能是 null 可能是 undefined
// 定义没有赋值就是 undefined
let num: number | null | undefined;
⑨ void: 定义方法时,方法没有返回值,使用 void 作为返回值类型;
// 注意:方法返回值类型 不可以 写 undefined !!!
function run(): void {
console.log('runrunrunrun');
}
⑩ never:其他类型 (包括 null 和 undefined)的子类型,从不会出现的值,只能被 never 赋值;
let a: never;
a = 123; // 错误
a = (() => {
throw new Error('错误');
}) ();
3.TypeScript 函数、参数、重载
3.1 函数、参数
3.1.1 函数定义
函数声明法、匿名函数法
// 函数声明法 参数指定类型,返回值也要指定类型
function getInfo(name: string, age: number): string {
// ES6模板字符串语法 返回值要和指定类型一致
return `${name} --- ${age}`;
}
alert(getInfo('zhangsan', 20));
// 匿名函数法
const getInfo = function (name: string, age: number): string {
return `${name} --- ${age}`;
}
alert(getInfo('zhangsan', 40));
// 没有返回值 void
function run(): void {
console.log('runrunrunrun')
}
3.1.2 可选参数 和 默认参数
es5 里实参形参可以不一样,但是 ts 中实参、形参必须一样;如果实参、形参不一样,就需要配置 可选参数,且 可选参数 必须配置到 必选参数 的后面
es5 里没法设置默认参数值,es6 和 ts 中,可以设置默认参数值;
// 可选参数 ? 就代表可选参数,必须写 “最后” !!!
function getInfo(name: string, age?: number): string {
if (age) {
return `${name} --- ${age}`;
} else {
return `${name} --- 年龄保密`;
}
}
alert(getInfo('zhangsan'))
alert(getInfo('zhangsan', 123))
// 默认参数(es6、ts)
function getInfo(name: string, age: number = 20): string { ... }
3.1.3 剩余参数
// 剩余参数
// ...result: number[] 含义:将剩余参数存储在 result(可变名字)数字类型数组中
function sum(a: number, b: number, ...result: number[]): number {
let sum = a + b;
for (let i = 0; i < result.length; i++) {
sum += result[i];
}
return sum;
}
alert(sum(1,2,3,4,5,6)) ;
3.2 重载、重写
3.2.1 重载(Java VS TypeScript VS ES5)
Java 中的重载:两个或者两个以上 同名函数,但参数不一样
TypeScript 中的重载:同一个函数,提供多个函数类型定义
ES5 没有重载的说法,若出现同名方法,下面的会替换上面的方法( 即使参数个数不同 )
// TypeScript 中的方法重载
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;
}
}
3.2.2 重写
Java 中的重写:重写是 子类对父类 的 某方法 重写逻辑,返回值和形参都不能变(外壳不变,核心重写)
4.TypeScript 类、继承、多态、抽象类
4.1 类、静态 / 实例属性、静态 / 实例方法、原型链上的属性方法
- 实例属性 / 实例方法:定义在构造函数里,通过实例化类的对象,使用对象进行调用
- 静态属性 / 静态方法:定义在类名上,通过类名直接调用
4.1.1 ES5 中的类、静态 / 实例属性、静态 / 实例方法、原型链上的属性方法
// ES5
function Person() {
/* 实例属性:构造函数中的属性 */
this.name = '张三';
/* 实例方法:构造函数中的方法 */
this.run = function () {
alert(this.name + '在运动');
}
}
// 调用实例方法:先实例化类,然后对象调用
var p = new Person();
p.work();
/* 静态方法:定义在类名上的方法 */
Person.getInfo = function () {
alert('静态方法:定义在类名上的方法');
}
// 调用静态方法:直接通过类名调用
Person.getInfo();
// 原型链上面的属性会被多个实例共享,构造函数不会
/* 原型链上的属性 */
Person.prototype.sex = "男";
/* 原型链上的方法 */
Person.prototype.work = function () {
alert(this.name + '在工作');
}
4.1.2 TypeScript 中的类、静态 / 实例属性、静态 / 实例方法
// TypeScript
class Per {
/* 实例属性 */
public name: string;
public age: number = 20;
/* 静态属性 */
static sex = "男";
constructor(name: string) {
this.name = name;
}
/* 实例方法 */
run() {
alert(`${this.name}在运动`)
}
/* 静态方法 */
static print() {
/* 不能直接调用类里面的属性(sex)要写成(Per.sex) */
alert('print方法' + Per.sex);
}
}
let p = new Per('张三');
/* 实例方法:通过 实例化类的对象 调用 */
p.run();
/* 静态方法:通过 类名 直接调用 */
Per.print();
/*静态属性:通过 类名 直接调用*/
alert(Per.sex);
4.2 对象冒充继承、原型链继承、原型链 + 对象冒充组合继承
- 原型链上面的属性会被多个实例共享(即创建新的实例后,不需要重新开辟空间存储上面的变量),构造函数不会
- 原型链继承、对象冒充继承、原型链+对象冒充组合继承:
- 对象冒充继承:可以继承*构造函数*里面的属性和方法,不能继承*原型链*上面的属性和方法(实例化子类可以传参)
- 原型链继承:可以继承*构造函数*里面的属性和方法,也可以继承*原型链*上面的属性和方法(实例化子类无法传参)
- 原型链+对象冒充组合继承:可以继承*构造函数*里面的属性和方法,也可以继承*原型链*上面的属性和方法(实例化子类可以传参)
// Web类 继承 Person类
function Web() {
Person.call(this); /*对象冒充实现继承*/
}
var w = new Web();
w.run(); // 对象冒充可以继承构造函数里面的属性和方法
w.work(); // 对象冒充没法继承原型链上面的属性和方法
————————————————————————————————————————————————————————————————————————————
function Web() {}
Web.prototype = new Person(); /*原型链实现继承*/
var w = new Web(); // 可以继承构造函数里面的属性和方法 也可以继承原型链上面的属性和方法
w.run();
w.work();
————————————————————————————————————————————————————————————————————————————
function Web(name,age) {} /*原型链实现继承 缺点*/
Web.prototype = new Person();
var w = new Web('赵四',20); // 实例化没法*传参*
w.run();
————————————————————————————————————————————————————————————————————————————
function Web(name,age){ /*原型链+对象冒充组合继承*/
Person.call(this,name,age); // 对象冒充继承 实例化子类可以给父类传参
}
Web.prototype = new Person(); // 原型链继承 可以继承原型链上的方法
Web.prototype = Person.prototype; // 这么写也行
var w = new Web('赵四',20);
4.3 类的定义和继承(extends / super)
- TS中实现继承:extends super
- 构造函数:实例化类的时候触发的方法
function Person(name){ // ES5 中定义类 this.name = name; this.run = function(){ console.log(this.name) }} var p = new Person('张三'); p.run() —————————————————————————————————————————————————————————————————————————— class Person { // TS 中定义类 name:string; // 属性定义 前面省略了 public关键词 constructor(name:string) { // 构造函数 实例化类的时候触发的方法 this.name = name; } getName():string { return `${this.name}在运动`; } setName(name:string):void{ this.name = name; }} var p = new Person('张三'); p.setName('李四'); alert(p.getName()); —————————————————————————————————————————————————————————————————————————— class Web extends Person{ // TS中实现继承 extends super constructor(name:string){ super(name); /* 初始化父类的构造函数 */ }} var w = new Web('赵六'); alert(w.getName());
4.4 类修饰符
- public:公有 —— 任何类都能访问(不写默认是 public)
- protected:保护类型 —— 当前类及子类可访问
- private:私有 —— 当前类可访问
4.5 多态(属于继承)
- 多态:父类定义一个方法不去实现,让继承它的子类去实现 每一个子类有不同的表现
class Animal { // 父类 name:string; constructor(name:string) { this.name = name; } eat(){ // 具体吃什么不知道,具体吃什么?继承它的子类去实现 console.log('吃的方法') }} class Dog extends Animal{ // 子类1 constructor(name:string){ super(name) } eat(){ // 子类多态的样子 return this.name+'吃粮食' }} class Cat extends Animal{ // 子类2 constructor(name:string){ super(name) } eat(){ // 子类多态的样子 return this.name+'吃老鼠' }}
4.6 抽象类 abstract
- 抽象类中的抽象方法*不包含*具体实现,必须在*派生类*中实现,抽象方法只能放在抽象类里
- 抽象类和抽象方法用来*定义标准*(标准:Animal 这个类要求它的子类必须包含 eat 方法)
- 抽象类:提供其他类继承的基类,不能直接被实例化,不能实现抽象方法
- 抽象类的派生子类:必须实现抽象方法,可以实例化
abstract class Animal{ // 抽象类:定义标准,不能实现抽象方法,不可以实例化 public name:string; constructor(name:string){ this.name = name; } abstract eat():any; // 抽象方法*不包含*具体实现,并且必须在派生类中实现 run(){ console.log('其他方法可以不实现') }} // var a = new Animal() /*错误的写法 抽象类不可以实例化!!!!*/ class Dog extends Animal{ // 抽象类的派生类:必须实现抽象方法,可以实例化 constructor(name:any){ super(name) } eat(){ // 抽象类的子类必须实现抽象类里的抽象方法 console.log(this.name+'吃粮食') }} var d = new Dog('小花花'); d.eat();
5.Typescript 接口
- 接口的作用:行为和动作的规范,对批量方法进行约束,只规定这批类里必须提供某些方法,不规定方法内容
- Typescrip 中的接口类似于 Java,还增加了更灵活的接口类型,包括属性、函数、可索引和类
5.1 属性类型接口
- 属性类接口定义、属性类接口规范JSON格式、属性类接口规范封装 Ajax:
interface FullName { // 属性接口 firstName:string; // 注意 ; 结束 secondName?:string; // 可选属性 } function printName(name:FullName){ // 用到属性接口的方法定义:必须传入对象FullName console.log(name.firstName+'--' + name.secondName); } // printName('1213'); // 错误,传入参数不合法 var obj = { /* 传入的参数必须包含 firstName */ age:20, // 其他参数 firstName:'张', secondName:'三' // 可选参数,可写可不写 }; printName(obj) // 正确的方法调用1 printInfo({ // 正确的方法调用2 参数的顺序可以不一样 secondName:'四', firstName:'李' }) —————————————————————————————————————————————————————————————————————————————————— ajax({ // 属性接口应用于 Ajax type:'get', data:'name=zhangsan', url:'http://a.itying.com/api/productlist', //api dataType:'json' }) —————————————————————————————————————————————————————————————————————————————————— interface Config { // 配置属性接口 type:string; url:string; data?:string; dataType:string; } —————————————————————————————————————————————————————————————————————————————————— function ajax(config:Config) { // 原生js封装的 Ajax 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){ console.log('chengong'); if(config.dataType == 'json'){ console.log(JSON.parse(xhr.responseText)); }else{ console.log(xhr.responseText) }}}} —————————————————————————————————————————————————————————————————————————————————— // ts中自定义方法传入参数,对 JSON 进行约束 function printLabel(labelInfo:{label:string}):void { console.log('printLabel'); } printLabel('hahah'); // 错误写法 printLabel({name:'张三'}); // 错误的写法 printLabel({label:'张三'}); // 正确的写法
5.2 函数、可索引、类类型接口
- 函数类型接口:对方法传入的参数、返回值进行约束
- 可索引接口:数组、对象的约束 (不常用)
- 类类型接口:对类的约束,类似于抽象类抽象
// 函数类型接口:对方法传入的参数、返回值进行约束 批量约束 interface encrypt{ (key:string, value:string):string; } // 函数 md5 采用 encrypt 接口定义的加密方式 var md5:encrypt = function(key:string,value:string):string{ return key + value; } console.log(md5('name', 'zhangsan')); —————————————————————————————————————————————————————————————————————————————————— // 可索引接口 对数组的约束 interface UserArr { // 对数组的约束 [index:number]:string } // var arr:UserArr = ['aaa','bbb']; // console.log(arr[0]); var arr:UserArr = [123,'bbb']; /*错误*/ —————————————————————————————————————————————————————————————————————————————————— // 可索引接口 对对象的约束 interface UserObj { [index:string]:string } var arr:UserObj = {name:'张三'}; —————————————————————————————————————————————————————————————————————————————————— // 类类型接口 interface Animal{ name:string; eat(str:string):void; } class Cat implements Animal{ name:string; constructor(name:string){ this.name = name; } eat(food:string){ console.log(this.name + '吃' + food); }} var c = new Cat('小花'); c.eat('老鼠');
5.3 接口扩展、接口继承
- 接口可以继承接口
interface Animal { // 接口扩展:接口可以继承接口 eat():void; } interface Person extends Animal { work():void; } class Web implements Person{ public name:string; constructor(name:string){ this.name = name; } eat() { console.log(this.name + '喜欢吃馒头') } work(){ console.log(this.name + '写代码'); }} —————————————————————————————————————————————————————————————————————— // 同时继承类和接口 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代码');
6.Typescript 泛型、命名空间
6.1 泛型
- 泛型就是解决:类、接口、方法的 复用性、以及对不特定数据类型的支持(类型校验)
- 同时返回 string类型 和 number类型,any可以解决,any放弃了类型检查,传入什么就返回什么
- T表示泛型,支持不特定的数据类型,具体什么类型是调用方法的时候决定的
// 泛型定义 function getData<T>(value:T):T{ // 这样就可以同时处理两套数据类型,防止冗余 return value; } getData<number>(123); getData<string>('1214231'); ———————————————————————————————————————————————————————————————————————————————————— // 泛型类 class MinClas<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 MinClas<number>(); /*实例化类 并且指定了类的T代表的类型是number*/ m1.add(11); m1.add(3); alert(m1.min()) var m2=new MinClas<string>(); /*实例化类 并且指定了类的T代表的类型是string*/ m2.add('a'); m2.add('v'); alert(m2.min()) —————————————————————————————————————————————————————————————————————————————————————— // 泛型接口 interface ConfigFn<T>{ (value:T):T; } function getData<T>(value:T):T{ return value; } var myGetData:ConfigFn<string> = getData; myGetData('20'); /*正确*/ // myGetData(20) // 错误
6.2 命名空间
- 代码量较大的情况下,为避免变量命名冲突,可将相似功能的函数、类、接口等放置到命名空间内
- 同 Java 的包、Net 的命名空间一样,TypeScript 的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。命名空间内的对象通过 export 关键字对外暴露
- 命名空间和模块的区别:
- 命名空间:内部模块,主要用于组织代码,避免命名冲突
- 模块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间
- 命名空间的导入导出也是 export / import
- modules / animal.ts:
export namespace A { // namespace 命名空间 导出命名空间也采用 export interface Animal { name: string; eat(): void; } export class Dog implements Animal { // 导出类 name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃狗粮。`); }} export class Cat implements Animal { // 导出类 name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 吃猫粮。`); }}} export namespace B {...} // B 里可以有和 A 里名字相同的类
- index.ts
import {A,B} from './modules/animal'; // 导入两个命名空间 import var d = new A.Dog('小黑'); d.eat(); var dog = new B.Dog('小花'); dog.eat();
7.Typescript 模块、模块封装数据库接口
7.1 模块基本概念
- 可以把公共功能单独抽离成一个文件作为一个模块,模块里的变量、函数、类等默认是私有的,如果要在外部访问模块里面的数据(变量、函数、类),需要通过 export 暴露模块 里的数据(变量、函数、类...),暴露后通过 import 引入模块,就可以使用模块里暴露的数据(变量、函数、类...)
- 一些名词区分:
- export:在需要导出的变量前面加,也可以简写: export {xx, xx, xx}
- 如果是 export,需要加 import {} 引入需要的内容(花括号一定不能忘记)
- import {xx as xx}:as 对引入的内容重命名
- export default 默认导出:每个模块都可以有一个 default 默认导出,并且一个模块只能有一个 default 默认导出
- 需要使用一种特殊的导入形式来导入 default 默认导出:就是不加花括号
- modules/db.ts:
var dbUrl = 'xxxxxx'; export function getData():any[] { console.log('获取数据库的数据111'); return [ { title:'121312' },{ title:'121312' }] } export function save() { console.log('保存数据成功'); } // export {dbUrl, getData, save}; // 这是 export 简写 // export default getData; // export default 默认导出
- index.ts:
import { getData, save } from './modules/db'; // 如果是 export,需要加 {} 引入需要的内容 // import { dbUrl, getData as get} from './modules/db'; // as 对引入的内容重命名 // import getData from './modules/db'; // 这是默认导出 引入的时候不可以添加花括号 getData(); // console.log(dbUrl);
7.2 模块化封装数据库接口
- 功能:定义一个操作数据库的库,支持 MySql、MsSql、MongoDB
- 要求:MySql、MsSql、MongoDB 功能一样 都有 add、update、delete、get 方法
- 注意:约束统一规范(所以要定义接口)、代码重用(所以用到泛型)
- 接口:定义了行为和动作的规范
- 泛型:解决 类 接口 方法的复用性
- modules / db.ts:
// 定义泛型,实现各种复用 interface DBI<T>{ add(info:T):boolean; update(info:T,id:number):boolean; delete(id:number):boolean; get(id:number):any[]; } ———————————————————————————————————————————————————————————————————————————— // 定义操作 MySql 数据库的类 // 注意:要实现泛型接口,这个类也应该是一个泛型类 export class MysqlDb<T> implements DBI<T> { constructor(){ console.log('数据库建立连接'); } add(info:T):boolean { console.log(info); return true; } update(info:T, id:number):boolean { throw new Error("Method not implemented."); } delete(id:number):boolean { throw new Error("Method not implemented."); } get(id:number):any[] { var list = [ { title:'xxxx', desc:'xxxxxxxxxx' },{ title:'xxxx', desc:'xxxxxxxxxx' }] return list; }} ———————————————————————————————————————————————————————————————————————————— // 定义操作 MsSql 数据库的类 export class MsSqlDb<T> implements DBI<T>{...} // 代码基本同上
- model / article.ts:
import {MsSqlDb} from '../modules/db'; class ArticleClass{ // 定义数据库映射 title:string | undefined; desc:string | undefined; } var ArticleModel = new MsSqlDb<ArticleClass>(); export { ArticleClass, ArticleModel }
- model / user.ts:
import {MsSqlDb} from '../modules/db'; class UserClass { // 定义数据库的映射 username:string | undefined; password:string | undefined; } var UserModel = new MsSqlDb<UserClass>(); export { UserClass, UserModel }
- index.ts:
import {UserClass,UserModel} from './model/user'; import {ArticleClass,ArticleModel} from './model/article'; // 增加数据 var u = new UserClass(); u.username = '张三'; u.password = '12345655654757'; UserModel.add(u); // 获取user表数据 var res = UserModel.get(123); console.log(res); // 获取文章表的数据 var aRes = ArticleModel.get(1); console.log(aRes);
8.Typescript 装饰器
- 装饰器:一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能
- 装饰器分类:类装饰器、属性装饰器、方法装饰器、参数装饰器
- 装饰器写法:普通装饰器(无法传参)、装饰器工厂(可传参)
8.1 类装饰器
- 类装饰器:用于类构造函数,可以用来监视、修改或替换类定义
- 普通装饰器(无法传参):没有 return function() {...}
// 类装饰器的定义 function logClass(params:any) { console.log(params); // params 就是当前类 // 在原型链上添加新的属性方法 params.prototype.apiUrl = '动态扩展的属性'; params.prototype.run = function(){ console.log('动态扩展的方法'); }} @logClass // 类装饰器紧挨着类的上面写 class HttpClient { constructor() {} getData() {} } // 实例化类,调用装饰器里新添加的内容 var http:any = new HttpClient(); console.log(http.apiUrl); http.run();
- 装饰器工厂(可传参):有 return function() {...}
function logClass(params:string) { // 装饰器工厂 return function return function(target:any) { console.log(target); // 相当于当前类 console.log(params); // 相当于传入的参数 target.prototype.apiUrl = params; // 在当前类的原型上添加属性接受参数 }} @logClass('http://www.itying.com/api') // 接受参数的类装饰器 class HttpClient { constructor() {} getData() {} } var http:any = new HttpClient(); console.log(http.apiUrl);
- 重载构造函数:return class extends target {...)
- 类装饰器表达式会在运行时当作函数被调用,类构造函数为其唯一参数,如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明
function logClass(target:any) { console.log(target); // 接受类本身 return class extends target { // 返回一个类继承类,即改写类的内容 apiUrl:any = '我是修改后的数据'; getData() { this.apiUrl = this.apiUrl + '----'; console.log(this.apiUrl); }}} @logClass class HttpClient { public apiUrl:string | undefined; constructor() { this.apiUrl = '我是构造函数里面的apiUrl'; } getData(){ console.log(this.apiUrl); }} var http = new HttpClient(); http.getData();
8.2 属性装饰器
- 属性装饰器:运行时被当作函数来调用,传入两个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 成员名字
function logProperty(params:any){ // 传入参数 return function(target:any, attr:any){ console.log(target); // 类的构造函数/原型对象 console.log(attr); // 属性成员名字 target[attr] = params; // 类的构造函数/原型对象[属性成员名字] = 传入参数 }} @logClass('xxxx') // 类装饰器 class HttpClient{ @logProperty('http://itying.com') // 属性装饰器 public url: any | undefined; // url 就是属性成员名字 constructor() {} // 类的构造函数 getData() { console.log(this.url); }} var http = new HttpClient(); http.getData();
8.3 方法装饰器
- 方法装饰器:用来监视,修改、替换方法定义,传入三个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 成员名字
- 成员属性描述符
function get(params:any) { return function(target:any, methodName:any, desc:any) { console.log(target); // 类的构造函数/原型对象 console.log(methodName); // 方法成员名 console.log(desc); // 方法属性描述符 target.apiUrl = 'xxxx'; target.run = function() { console.log('run'); }}} class HttpClient { public url: any | undefined; constructor() {} // 方法装饰器 @get('http://www.Lyrelion.com') getData() { console.log(this.url); }} var http:any = new HttpClient(); console.log(http.apiUrl); http.run(); =================================================================================== function get(params:any) { return function(target:any, methodName:any, desc:any){ console.log(target); console.log(methodName); console.log(desc.value); // 修改装饰器的方法,把装饰器方法里面传入的所有参数改为 string 类型 // 1、保存当前的方法 var oMethod = desc.value; desc.value = function(...args:any[]) { args = args.map((value) => { return String(value); }) oMethod.apply(this,args); }}} class HttpClient { public url : any | undefined; constructor() {} @get('http://www.Lyrelion.com') getData(...args:any[]){ console.log(args); console.log('我是getData里面的方法'); }} var http = new HttpClient(); http.getData(123,'xxx');
8.4 方法参数装饰器
- 方法参数装饰器:在运行时当作函数被调用,传入三个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 参数名字
- 参数在函数参数列表中的索引
function logParams(params:any) { return function(target:any, methodName:any, paramsIndex:any){ console.log(params); console.log(target); console.log(methodName); console.log(paramsIndex); target.apiUrl = params; }} class HttpClient { public url : any | undefined; constructor() {} getData(@logParams('xxxxx') uuid:any) { // 方法参数装饰器 console.log(uuid); }} var http:any = new HttpClient(); http.getData(123456); console.log( http.apiUrl );
8.5 装饰器执行顺序
- 属性 > 方法 > 方法参数 > 类
- 如果有多个同样的装饰器,会先执行后面的
function logClass1(params:string){ return function(target:any){ console.log('类装饰器1') }} function logClass2(params:string){ return function(target:any){ console.log('类装饰器2') }} function logAttribute1(params?:string){ return function(target:any,attrName:any){ console.log('属性装饰器1') }} function logAttribute2(params?:string){ return function(target:any,attrName:any){ console.log('属性装饰器2') }} function logMethod1(params?:string){ return function(target:any,attrName:any,desc:any){ console.log('方法装饰器1') }} function logMethod2(params?:string){ return function(target:any,attrName:any,desc:any){ console.log('方法装饰器2') }} function logParams1(params?:string){ return function(target:any,attrName:any,desc:any){ console.log('方法参数装饰器1') }} function logParams2(params?:string){ return function(target:any,attrName:any,desc:any){ console.log('方法参数装饰器2') }} @logClass1('http://www.Lyrelion.com/api') // 类装饰器 @logClass2('xxxx') class HttpClient { @logAttribute1() // 属性装饰器 @logAttribute2() public apiUrl : string | undefined; constructor(){} @logMethod1() // 方法装饰器 @logMethod2() getData() { return true; } setData(@logParams1() attr1:any, @logParams2() attr2:any) {} // 方法参数装饰器 } var http:any = new HttpClient();
9.Typescript 补充内容
- npm install -g typescript (在全局环境下安装
tsc
命令)- 使用 TypeScript 编写的文件以
.ts
为后缀,用 TypeScript 编写 React 时,以.tsx
为后缀- 运行 tsc hello.ts 出现问题:
- 解决方案:以管理员身份 打开windows powershell 输入set-ExecutionPolicy RemoteSigned 选择 A 或者 Y
- TypeScript 编译的时候即使报错了,还是会生成编译结果,仍然可以使用这个编译之后的文件
- 如果要在报错的时候终止 js 文件的生成,可以在
tsconfig.json
中配置noEmitOnError
boolean
是 JavaScript 中的基本类型Boolean
是 JavaScript 中的构造函数// 布尔型 let isDone: boolean = false; // 编译成功 let createdByNewBoolean: boolean = new Boolean(1); // 编译失败,使用构造函数创建的不是布尔值 let createdByNewBoolean: Boolean = new Boolean(1); // 编译成功,使用构造函数返回的是布尔对象 let createdByBoolean: boolean = Boolean(1); // 编译成功,直接调用函数,不new,返回布尔值 // 数字型 let octalLiteral: number = 0o744; // ES6 中的八进制表示法 let notANumber: number = NaN; let infinityNumber: number = Infinity; // 字符串型 let myName: string = 'Tom'; let myAge: number = 25; let sentence: string = `Hello, my name is ${myName}. I'll be ${myAge + 1} years old next month.`; // 模板字符串
- 声明为空值 void 的变量,只能被赋值为 null 和 undefined,比如: let unusable: void = undefined;
undefined
和null
可以赋值给 任意类型 的数据,而 void 不可以- let num: number = undefined; // 不报错
- let num: number = void; // 报错