TypeScript 学习笔记

TS 简介

Ts 微软开发,包含 ES6、包含 ES5

编译 tsc xx.ts
每一次都得编译,可以自动编译

开发工具中配置 typescirpt 自动编译

vscode:

  1. 创建 tsconfig.json 文件 tsc --init 生成配置文件
  2. tsconfig.json 配置文件中,修改 outDir 配置项,取消注释然后修改为.js
  3. vscode 中,点击上方栏位 run task,选择 ts 监听
  4. 完成

TS 类型

  • 与 es5 中的区别
// es5:类型变化不报错
var flag = true;
flag = 234;

// ts:必须指定类型
typescript;
var flag: boolean = true;
flag = 131; //报错

TS 类型:

  • boolean
  • number
  • string
  • array 数组:
    方式 1:var arr:number[] = [1,2,3]//制定 arr 里面全是数字
    方式 2:var arr:Array= [1,2,3]

元组类型(tuple)

  • 方式 1:属于数组的一种,即数组中每一个元素指定类型
  • 方式 2:var arr:[number, string]=[123,“this is ts”];

枚举类型(enum)

// 常用来标识状态码
enum Flag {
success = 1,
error = 2,
}
let f: Flag = Flag.error;
console.log(f); // 2

// 如果 没有标识符没有赋值,那么打印的就是下标
enum Color {
blue,
red,
orange,
}
var c: Color = Color.red;
console.log(c); //1,下标
enum Color {
blue,
red = 3,
orange,
}
var c: Color = Color.red;
console.log(c); //3

// 常用来标识状态码
enum Err {
'undefined' = -1,
'null' = -2,
}
var c: Err = Err.null;
console.log(c); // -2

任意类型 any

  • 类似 ES5 不指定变量类型的 var
var num: any = 123;
num = true; // 不报错

null 类型和 undefined 其他数据类型的子类型

  • 变量定义之后没有赋值,报 undefined
// 一个元素可能是number类型,可能是null或者undefined
var num: number | undefined | null;

void 和 java 一样 没有返回值类型

// 如果方法没有返回值
function run(): void {
console.log('asdf');
}
// 如果方法有返回值:
function run(): number {
return 1;
}

never 类型,代表从来不会出现的值,是其他类型(包括 null‘和 undefined)的子类型,代表从不会出现的值

  • 自己理解为上述类型之外的数据类型
// 如下,接收Err类型的数据
var a:never;
a = undefined;//报错
a = (()=>{
new Throw Err("报错")
})()

函数的定义

  • ES5 中:
// 函数声明法
function run(){
return ...
}
//匿名函数
var run2 = function(){
return ..
}
  • TS 中:
//函数声明法
function run(): number {
return 123;
}
// 匿名函数
var fun2 = function (): number {
return 123;
};

ts 中定义方法传参

function getInfo(name: string, age: number): string {
return name + ' ' + age;
}
var getInfo = function (name: string, age: number): string {
return name + age;
};

方法可选参数

es5 里方法实参和形参可以不一样,但是 ts 必须一致,如果不一样就需要配置可选参数

  • 参数后边加?可以设置参数可选传
  • 可选参数必须配置到参数的最后边
function getInfo(name:string, age?number):string{
return
}

默认参数

// 默认参数,直接在形参赋值
function getInfo(name:string, age:number=20):string{
return
}

剩余参数

function sum(a:number, b:number, c:number, d:number):number{
return a+b+c+d;
}

// 三点运算符:接收不固定参数的(剩余参数)的值
function sum(…rest:number[]):number{
var sum= 0 ;
for(var i=0; i<rest.length;i++){
sum+=rest[i];
}
return sum;
}

函数重载

  • 类似 java,同名但是不同参数的多个函数方法
  • ts 为了兼容 es5,以及 es6,和 java 有区别
  • es5 中,出现同名方法时候,下面的方法会替换上面的方法
  • ts 中的重载:
function getInfo(name: string): string;
function getInfo(age: number): string;
function getInfo(str: any): any {
if (typeof str === 'string') {
return '我叫:' + str;
} else {
return '我的年龄是:' + str;
}
}

箭头函数

箭头函数里面的 this 指向上下文

1、ES5 中定义类:

function Person() {
this.name = '张三';
this.age = 20;
}
var p = new Person();
alert(p.name);

2、构造函数和原型链里面定义

// 声明的构造方法
function Person(){
this.name = "张三";
this.age=20;
this.run = function()){
alert(this.name+"在运动");
}
}
// 原型链的属性和方法
Person.prototype.sex="男";
Person.prototype.work=function(){
alert(xx)
}
var p = new Person();
p.work();

3、静态方法

4、es5 中的继承-对象冒充继承

// 要实现Web类 继承 Person类  原型链+对象冒充的组合继承模式
function Person(){
this.name = "张三";
this.age=20;
this.run = function()){
alert(this.name+"在运动");
}
}
// 原型链的属性和方法
Person.prototype.sex="男";
Person.prototype.work=function(){
alert(xx)
}
// 要实现Web类 继承 Person类
function Web(){
Person.call(this); //对象冒充继承
}

var w = new Web();
w.run();//执行父类Person的run,对象冒充可以继承构造函数里面的属性和方法
w.work();// 对象冒充可以继承构造函数的属性和方法 但是没办法继承原型链的属性和方法(prototype)
  • 关于 call:
function add(c, d) {
return this.a + this.b + c + d;
}

const obj = { a: 1, b: 2 };

console.error(add.call(obj, 3, 4)); // 10
大统上的说法就是,call改变了this的指向。然后,介绍this xxx什么一大堆名词,反正不管你懂不懂,成功绕晕你就已经ok了,但是实际发生的过程,可以看成下面的样子。

const o = {
a: 1,
b: 2,
add: function(c, d) {
return this.a + this.b + c + d
}
};

给o对象添加一个add属性,这个时候 this 就指向了 o,
o.add(5,7)得到的结果和add.call(o, 5, 6)相同。

5、原型链继承方法

function Person(){
this.name = "张三";
this.age=20;
this.run = function()){
alert(this.name+"在运动");
}
}
// 原型链的属性和方法
Person.prototype.sex="男";
Person.prototype.work=function(){
alert(xx)
}
// web原型链方式继承 person
function web(){

}
web.prototype= new person();// 原型链继承

web.work();// 可以工作,可以继承原型链属性方法

6、原型链实现继承的问题??无法给父类传参

function Person(){
this.name = "张三";
this.age=20;
this.run = function()){
alert(this.name+"在运动");
}
}
// 原型链的属性和方法
Person.prototype.sex="男";
Person.prototype.work=function(){
alert(xx)
}
var p = new person('李四', 20);
p.run(); // 没问题
// 继承,无法给父类传参
function Web(name,age){

}
Web.prototype= new Person();
var w = new Web('sss', 20);
w.run();// 父类会alert出来“undefiend在运动”
// 实例化子类时候没法给父类传参

7、原型链+构造函数的组合继承模式

function Person(){
this.name = "张三";
this.age=20;
this.run = function()){
alert(this.name+"在运动");
}
}
// 原型链的属性和方法
Person.prototype.sex="男";
Person.prototype.work=function(){
alert(xx)
}
var p = new person('李四', 20);
p.run(); // 没问题
// 继承,无法给父类传参
function Web(name,age){
Person.call(this,name,age); // 对象冒充继承 可以继承构造函数里面的属性和方法 实例化子类可以给父类传参
}
Web.prototype = new Person();// 实例化
var w = new Web('sss', 20);
w.run();// 父类会alert出来“undefiend在运动”
// 实例化子类时候没法给父类传参

8、原型链+对象冒充的另一种写法

function Person(){
this.name = "张三";
this.age=20;
this.run = function()){
alert(this.name+"在运动");
}
}
// 原型链的属性和方法
Person.prototype.sex="男";
Person.prototype.work=function(){
alert(xx)
}
var p = new person('李四', 20);
p.run(); // 没问题
// 继承,无法给父类传参
function Web(name,age){
Person.call(this,name,age); // 对象冒充继承 可以继承构造函数里面的属性和方法 实例化子类可以给父类传参
}
Web.prototype = Person.prototype; // 和方法7中不同的是这里!!!
var w = new Web('sss', 20);
w.run();// 父类会alert出来“undefiend在运动”
// 实例化子类时候没法给父类传参

类的定义

1、ts 中定义类,类似 java:

class Person(){
name:string; //属性 前面省略了private
construtor(n:string){
this.name = n;
}
run():void{
log(this.name);
}
}

2、继承

class Web extends Person {
constructor(name: string) {
super(name);
}
}

var w = new Web('李四');
alert(w.run());

3、类里面的修饰符

ts提供了三种修饰符:
public(默认的): 公有 在类里面、子类类外边都可以访问
protected:在类里面、子类里面可以访问、在类外部无法访问
private:在类里面可以访问、子类、类外部没法访问

静态属性 静态方法

function Person(){
this.run1=function(){// 实例方法

}
}

Person.run2=function(){} // 静态方法

调用实例方法:(实例化之后才能调用的)
var p = new Person();
p.run1();

调用静态方法:
Person.run2();

为什么会有静态方法和实例方法之分?
JQuery 的实例方法 css()方法和静态方法$.get()方法大概原码为:

// 生命一个节点/元素对象
function Base(element){
this.element = 获取dome节点的方法;
this.css = function(str, value){
this.element.style[str] = value;
}
}
// $方法去实例化这个BAse对象、实例方法
function $(element){
return new Base(element); // 实例化一个方法

}
// 静态方法get
$.get(){
。。。
}
// 那么css调用时候就可以这样写
实例方法:
$("#box").css("color", "red");

静态方法:
$.get('url', function(){

)
  • 另一种方式声明静态方法,利用 static 关键字声明:
class Person{
public name:string;
static sex = "男";
constructor(name:string){
this.name = name;
}
static print(){// 静态方法 里面没办法直接调用类里面的属性,
alert("静态方法:"+Person.sex);// 如果调用this.name就会报错!!!
}
}
// $.get(){// jq里面的get就是静态方法

}

多态

父类定义一个方法不去实现,让继承他的子类去实现,每一个子类都有不同的表现

抽象方法

  • 用来定义一个标准
  • ts 中的抽象类,它是提供其他类继承的基类,不能直接被实例化
  • 用 abstract 关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
  • abstract 抽象方法只能放在抽象类中
  • 抽象类和抽象方法用来定义标准,基类要求他的子类必须包含某种方法
  • 抽象方法只能出现在抽象类中
  • 抽象类中必须包含至少一个抽象方法,不然没有意义
abstract class Animal {
// 省略构造方法
abstract eat(): any;
}

// 抽象类无法直接实例化
var a = new Animal(); // 这句话是错的

class Dog extends Animal {
// 省略构造方法
eat() {
console.log(this.name + '吃');
}
}

var d = new Dog('sss');
d.eat(); // sss吃

接口

也是定义标准,定义了行为和动作的规范

1、属性接口

// 定义了这个方法的参数是jsonObjec,而且必须有
function printLabel(labelInfo: { label: string }): void {
console.log(labelInfo.label);
}
printLabel('ssss'); // 错误

printLabel({ name: 'asdf' }); // 错误
printLabel({ label: 'sss' }); // 正确,打印sss

2、接口,对批量方法进行约束

// 对批量方法传入参数进行约束
// 传入对象的约束

// 声明类型
interface FullName {
firstName: string; // 注意是;结束
secondName: string;
}
// 方法名中引用FullName类型
function printName(name: FullName) {
log(name.firstName + ' ' + name.secondName);
}

// 调用方式一(下方调用方式是会报错的,interface定义的属性object只能包含firstName和secondName)
printName({
age: 20,
firstName: '张',
secondName: '三',
});

// 调用方式二,下方引入其他的object即可忽略多余参数
var obj = {
age: 20,
firstName: '张',
secondName: '三',
};
printName(obj); // 这个不报错

3、接口、可选属性,加?问号表示可传可不传

interface FullName {
firstName: string;
secondName?: string; // secondName可传可不传
}

4、模拟封装一个 ajax

interface Config {
type: string;
url: string;
data?: string;
dataType: string;
}

5、函数类型接口、对方法传入的参数、以及返回值进行约束、批量约束

  • 例子:定义加密的函数类型接口
interface encrypt {
// 定义了函数参数为string、value,返回string
(key: string, value: string): string;
}

var md5: encrypt = function (key: string, value: string): string {
// 实现encrypt类型的函数 return key+value;//模拟下
};

6、可索引接口:对数组、对象的约束

  • ts 定义数组方法:
var arr: number[] = [123, 234];
var arr1: Array<string> = ['123', '222'];
  • 对数组的约束,数组类型接口
interface UserArray {
// 表示数组中index必须是number,value必须是string
[index: numer]: string;
}
var arr: UserArray = ['123', '22312'];
  • 对对象的约束,对象类型接口
interface UserObj {
[index: string]: string;
}
var obj: UserObj = { name: '2342' };
  • 对类的约束,类类型接口,和抽象类有点相似
interface Animal {
// 规定实现类必须要有name属性和eat方法
name: string;
eat(str: string): void;
}
class Dog implements Animal {
name: string; // 若没此属性,ts会编译报错
constructor(name: string) {
this.name = name;
}
eat() {
log('eat');
}
}
  • 接口的扩展:接口可以继承接口
interface Animal {
eat(): void;
}

interface Person extends Animal {
work(): void;
}

class Web implements Person {
public name: string;
constructor(name: string) {
this.name = name;
}

// eat必须定义
eat() {
log(this.name + '吃');
}

// work也必须定义
work() {
log(this.name + '工作');
}
}

interface Animal {
eat(): void;
}

interface Person extends Animal {
work(): void;
}

class Programmer {
//构造方法省略

coding(code: string) {
log(this.name + ' ' + code);
}
}
class Web extends Programmer implements Person {
public name: string;
constructor(name: string) {
this.name = name;
}

// eat必须定义
eat() {
log(this.name + '吃');
}

// work也必须定义
work() {
log(this.name + '工作');
}
}

泛型

和 any 有什么区别?

  • any 放弃了类型检查
  • 如果想做到传入什么类型就返回什么类型,例如传入 number 就返回 number,这时候就可以使用泛型
function getData(value: any): any {
return ''; //什么类型都可以
}

泛型:

  • 软件工程中,我们不仅要创建一致的定义好的 API,同时也要考虑可重用性,组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能
  • 在像 c#和 java 中,可以使用泛型来创建可重用的组件,一个组件可支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件
  • 通俗理解:泛型就是解决 类 接口 方法的重用性、以及对不特定数据类型的支持
  • 可以支持不特定的数据类型
function getData<T>(value: T): T {
return value; //传入什么返回什么
}
// 这样调用
getData<number>(123123);
getData<string>('12131');

// 也可以写成:
function getData<T>(value: T): any {
return value; //传入什么返回什么
}
  • 泛型类,比如有个最小堆算法,需要同时支持返回数字和字符串两种类型,通过类的泛型来实现,示例:
// 定义泛型类
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(3);
m.add(2);
log(m.min()); // 2
  • 但是上边的只能传入数字类型,是否可以用泛型解决?可以:
class MinClass<T> {
public list: T[] = [];
add(num: T): void {
this.list.push(num);
}
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;
}
}
// 调用,实例化时候要先声明参数类型<bumber
var m1 = new MinClass<number>();
m1.add(2);
m1.add(4);
log(m.min()); // 2
函数类型接口
  • 指定特殊类型的方法:
interface ConfigFn{
(value1:string, value2:string):string;
}

var setData:ConfigFn=function(value1:string, value2:string):string{
return value1 + value2;
}
setData("name", "张三);
  • 泛型接口写法 1:
interface Config {
<T>(value: T): T;
}

var getData: ConfigFn = function <T>(value: T): T {
return value;
};

getData<string>('张三');
  • 泛型接口写法 2:
interface Config<T> {
(value: T): T;
}

function getData<T>(value: T): T {
return value;
}

var myGetData: ConfigFn<string> = getData;

myGetData('张三');
  • 把类作为参数来约束数据传入的类型
class User{
username:string | undefined;
password:string | undefined;
}
class MySqlDb{
add(user:User):boolean{
console.log(user);
retrun true;
}
}

// 调用
var u = new User();
u.username="张三";
u.password="123456";

var Db = new MySqlDb();
Db.add(u);// console.log(u)
  • 上述方法可以改为泛型类
// 操作数据库的泛型类,这样可以规范插入数据库数据的类规范
class MySqlDb<T>{
add(info:T):boolean{
log(info);
return true;
}
}

// 想给User表增加数据
// 1、定义一个User类 和数据库进行映射
class User{
username:string | undefined;
password:string | undefined;
}
var u = new User();
u.username= '张三';
u.password="2312"
var Db = new MySqlDb<User>();// 这一步很关键,要定义User类型
Db.add(u);

// 2、文章类,数据库映射
class Article{
title:string | undefined;
desc:string | undefined;
status:number | undefined;
constructor(params:{
title:string | undefined;
desc:string | undefined;
status?number | undefined;// status可选参数
}){
this.title=params.title;
this.desc=params.desc;
this.status=params.status;
}
}

// 调用
var a = new Article({
title:"分类",
desc:"111",
status:1
})

//类当前参数的泛型类
var Db = MySqlDB<Article>();// 指定类型
Db.add(a);// log a

实战:要实现 TS 封装统一操作 Mysql Mongodb Mssql 的底层库

// 先定义一个接口,用于提供各类型数据库规范
interface DBI<T>{
add(info:T):boolean;
update(info:T, id:number):boolean;
delete(id:number):boolean;
get(id:number):any[];
}

// 定义一个操作mysql的类,注意 要实现泛型接口 这个类也应该一定是个泛型类
class MysqlDb<T> implements DBI<T>{
add(info:T): boolean{
log(info);
}
update...
delete...
get...
}

// 调用 操作数据表,定义一个User类和数据库进行映射,并进行MySql数据的插入操作
class User{
username:string | undefined;
password:string | undefined;
}

var u = new User();
u.username = "张三";
u.password="213";

var oMysql = new MysqlDb<User>();// 声明User类型参数
oMysql.add(u);// 插入

模块

概念:

  • 把一些公共的功能单独抽离成一个文件作为一个模块
  • 模块里面的变量 函数 类等默认都是私有的,如果我们要在外部访问模块内的数据(函数、变量、类)
  • 我们就需要通过 export 暴露模块里面的数据
  • 然后其他地方通过 import 引入模块就可以使用模块内的数据

模块暴露 export:

//  方式一
export function a(){
...
}
// 方式二
function a(){
...
}
export { a }

模块导入 import:

import { a, a as alias } from 'xxx';
a();
alias();

模块默认导出 default,一个模块只能用一次
暴露:

export default a(){

}

引入(不用花括号):

import a from 'aaa';
a();

DB 库用模块化封装// 省略了,代码比较简单

TS 命名空间

内部模块,主要用于组织代码,避免命名冲突,
个人理解:模块之中再分模块
定义模块、并导出不同命名空间:

export namespace A{
interface Animal{
name: string;
eat(): void;
}
export Class Dog implements Animal{
name: string;
constructor(name:string){
this.name = name;
}
eat:void(){
log(this.name +"在空间A中吃狗粮")
}
}
}
export namespace B{
interface Animal{
name: string;
eat(): void;
}
export Class Dog implements Animal{
name: string;
constructor(name:string){
this.name = name;
}
eat:void(){
log(this.name +"在空间A中吃狗粮")
}
}
}

  • 调用:
import { A, B } from 'xxx';
var d = new A.Dog('小黑');
d.eat(); // 小黑在空间A中吃狗粮

var dog = new B.Dog('小花');
dog.eat(); // 小花在空间B中吃狗粮

装饰器

定义:

  • 装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或者参数上,可以修改类的行为。
  • 通俗的将装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
  • 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
  • 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
  • 装饰器是过去几年中 js 最大的成就之一,已经是 ES7 的标准特性之一

类装饰器:普通装饰器

function logClass(params: any) {
console.log(params);
// params就是当前类
params.prototype.apiUrl = '动态扩展的属性';
params.prototype.run = function () {
console.log('我是一个run方法');
};
}

@logClass // 类装饰器,普通装饰器,无法传参,默认吧class传入
class HttpClient {
constructor() {}
getData() {}
}

类装饰器:装饰器工厂

作用:

  1. 修改构造函数
  2. 扩展类属性和方法
    定义:
   function logClass(params:string){// params是下方传过来的参数
return function(target:any){// target相当于是默认传过来的
log(target);
log(params);
target.prototype.apiUrl = params;
}
}

@logClass("https://baidu.com")// 可以传参
class HttpClient{
constructor(){

}
getData(){

}
}

var http:any = new HttpClient();
console.log(http.apiUrl);// https://baidu.com

可以修改构造函数的写法
function logClass(target:any){
log(target);
return class extends target{
apiUrl:any = "我是修改后的新数据";

getData(){
this.apiUrl = this.apiUrl + "----";
log(this.apiUrl);
}
}
}

@logClass
class HttpClient{
public apiUrl:string | undefined;
constructor(){
this.apiUrl = "我是构造函数里面的apiUrl"
}
getData(){
log(this.apiUrl)

}

var http= new HttpClient();
http.getData();

属性装饰器

作用:

  1. 可以给属性赋值
// 类装饰器
function logClass(params: string) {
// params是下方传过来的参数
return function (target: any) {
// target相当于是默认传过来的
log(target);
log(params);
target.prototype.apiUrl = params;
};
}

// 属性装饰器
function logProperty(params: any) {
// 固定写法,参数中,target为类对象,attr为参数名称
return function (target: any, attr: any) {
log(target);
log(attr);
target[attr] = params;
};
}

@logClass('https://baidu.com') // 可以传参
class HttpClient {
// 这个属性修饰器的作用就是给url赋值初始值
@logProperty('http://baidu.com')
public url: any | undefined;
constructor() {}
getData() {}
}

var http: any = new HttpClient();
console.log(http.apiUrl); // https://baidu.com

方法装饰器

用的是最多的

function get(params: any) {
return function (target: any, methodName: any, desc: any) {
log(target); // 类属性
log(methodName); // 方法名字 getData
log(desc); // 方法的描述,desc.value是方法描述
target.apiUrl = 'xxx'; // 修改雷属性
target.run = function () {
log('run');
};
};
}
class HttpClient {
public url: any | undefined;
constructor() {}
@get('https://www.baidu.com')
getData() {
log(this.url);
}
}

var http: any = new HttpClient();
log(http.apiUrl); // https://www.baidu.com‘
http.run(); // log run
  • 修改当前的方法(主要作用是装饰方法,并把方法的参数给变换类型):
// 这个方法装饰其主要作用就是把参数都给格式化成string类型
function get(params: any) {
return function (target: any, methodName: any, desc: any) {
log(target); // 类属性
log(methodName); // 方法名字 getData
log(desc.value); // 方法

// 想修改下方法,装饰一下,让他们的所有参数变成string类型,并且打印出来
var oMethod = desc.value;
desc.value = function (...args: any[]) {
args = args.map((value) => {
return String(value);
});

// 利用apply进行对象冒充,对getdata进行修改,如果没有apply就相当于是把getData方法给替换掉了

oMethod.apply(this, args); // this就是指function(...args:any[])这个函数
};
};
}
class HttpClient {
public url: any | undefined;
constructor() {}
@get('https://www.baidu.com')
getData(...args: any[]) {
log(args);
log('我是getData方法');
}
}

var http: any = new HttpClient();
http.get(123, 'xxx');

// 就会先打印["123", "xxx"]后打印 我是getData方法

方法参数装饰器

用的比较少,类装饰器也可以实现这个功能

  • 运行时候当做函数被调用,可以使用参数张诗琪为累的原型增加一些元素数据,传入下列三个参数:
  • 1 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 2 方法的名字
  • 3 参数在函数参数列表中的索引
function logParams(params:any){
return function(target:any, methodName:any, paramsIndex:any){
log(params);// xxxx
log(target); // 原型对象
log(methodName);// getData
log(paramsIndex); // 0
}
}
class HttpClient{
public url:any | undefined;
constructor(){

}

getData(@logParams("xxxx") uuid:any){
log(uuid); // iii
}
}

var a = new HttpClient();
a.getData("iii");

先后输出:
1. xxxx
2. 原型对象
3. getData
4. 0
5. iii


装饰器执行顺序

当存在 多个装饰器时候:

  • 执行优先级:属性装饰器>方法装饰器>方法参数装饰器>类装饰器

  • 如果有多个同样的装饰器,它会先从后边执行

掘金:前端 LeBron

知乎:前端 LeBron

持续分享技术博文,关注微信公众号 👇🏻

img