TypeScript装饰器原理分析

文章目录

        • 1.前言
        • 2.装饰器原理
          • 2.1 类装饰器
          • 2.2 属性饰器
          • 2.3 方法装饰器(访问器set.get也属于方法)
          • 2.4 参数装饰器
        • 3.装饰器执行顺序

1.前言

TypeScript装饰器装饰器是一种特殊类型的声明,它能够被附加到类声明,属性, 访问符,方法或方法参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

2.装饰器原理

2.1 类装饰器

参数:

1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

function ClassDecorator(target: any) {
    console.log('测试类装饰器');
}

@ClassDecorator
class testClass {
    constructor() {
        console.log('类构造函数')
    }
}

这段代码非常的简单,就是一个类装饰器,我们看来下ts被解析js之后的源码!

var __decorate = (this && this.__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 ClassDecorator(target) {
    console.log('测试类装饰器');
}
var testClass = /** @class */ (function () {
    function testClass() {
        console.log('类构造函数');
    }
    testClass = __decorate([
        ClassDecorator
    ], testClass);
    return testClass;
}());

我们来简单的拆分下源码,分三个部分!

//第一部分 
var __decorate = (this && this.__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 ClassDecorator(target) {
    console.log('测试类装饰器');
}


//第三部分
var testClass = /** @class */ (function () {
    function testClass() {
        console.log('类构造函数');
    }
    testClass = __decorate([
        ClassDecorator
    ], testClass);
    return testClass;
}());

那么先从第一部分开始

//this指向是window,先判断window有没有__decorate,没有的话就定义它
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    //arguments的 要看第三部分调用的地方,__decorate([ClassDecorator], testClass)
    //所以这里的arguments只有2个值,c=2
    var c = arguments.length,
      //2<3,所以这里 r=target,并且声明了一个d变量, d=undefined
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,d;
       //这里是es6的语法,判断支持不支持es6的Reflect,并且Reflect.decorate的类型是个函数(据我所以es6的Reflect并没有这个decorate方法)
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else
    //不支持Reflect,所以跑到这个,然后循环这个decorators,还是要看第三部分的调用地方
    //[testClass]是个数组,循环里面的testClass(这里我只写了一个,其实也有可能会有很多个)
    // 根据判断得知   d=testClass,  r=d(r) || r   
    // r=d(r) || r  就是 r=ClassDecorator(testClass) || testClass 
    //意思就是看ClassDecorator有没有返回值,有的话r=这个返回值(ts里面会报错),没有的话r=testClass
        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;
     //这里用到了逗号运算符,最终返回r(没有返回值就返回这个类)
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};

第二部分其实就是装饰器,其实实际运行的时候类是它的参数,最后看有没有返回值(第一部分已经解释了)

function ClassDecorator(target) {
    console.log('测试类装饰器');
}

在看第三部分调用的地方

//自执行函数
var testClass = /** @class */ (function () {
    function testClass() {
        console.log('类构造函数');
    }
    //调用__decorate,第一部分已经说了
    testClass = __decorate([
        ClassDecorator
    ], testClass);
    //最后返回的是这个__decorate的返回值r,不懂再看第一部分的解释
    return testClass;
}());

所以类装饰的其实就是每次都执行这个函数

var r=ClassDecorator(testClass) || testClass  // 测试类装饰器
return r;

tips1:第一部分的for循环是 var i = decorators.length - 1; i >= 0; i--,所以当有多个了装饰器时,是倒序执行的!


function ClassDecoratorOne(target: any) {
    console.log('测试类装饰器1');//后执行
}

function ClassDecoratorTwo(target: any) {
    console.log('测试类装饰器2'); //先执行
}

@ClassDecoratorOne
@ClassDecoratorTwo
class testClass {
    constructor() {
        console.log('类构造函数')
    }
}
//测试类装饰器2
//测试类装饰器1

tips2:如果你类装饰器里面强制有返回值(虽然ts语法不允许,会报错,但是解析出来的js还是能执行的),因为上面说了如果没有返回值,就是返回这个类,如果有返回值,将返回这个返回值


function ClassDecoratorOne(target: any) {
    return '我是装饰器的返回值';
}

function ClassDecoratorTwo(target: any) {
    console.log('测试类装饰器2');
}

@ClassDecoratorOne
class testClass {
    constructor() {
        console.log('类构造函数')
    }
}


console.log(testClass); //你把整个类都改了,打印出来是"我是装饰器的返回值"
2.2 属性饰器

参数:

1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2. 属性的名称。

function propertyDecorator(propertyValue: string) {
    return function (target: testClass, propertyName: string) {
        target[propertyName] = propertyValue;
    }
}

class testClass {

    constructor() {
        console.log('构造函数')
    }

    @propertyDecorator('zhangsan')
   public test: string;
}
let p = new testClass();
console.log(p.test);

看下解析之后的源码:

var __decorate = (this && this.__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 propertyDecorator(propertyValue) {
    return function (target, propertyName) {
        target[propertyName] = propertyValue;
    };
}
var testClass = /** @class */ (function () {
    function testClass() {
        console.log('构造函数');
    }
    __decorate([
        propertyDecorator('zhangsan')
    ], testClass.prototype, "test", void 0);
    return testClass;
}());
var p = new testClass();
console.log(p.test);

解释一下 void 0是underfined的意思,因为test属性是public,所以第二个参数是类的原型对象,如果是静态属性,第二个参数就是类对象,接下来还是看第一部分

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    //属性装饰器是4个参数,第一个是装饰函数,第二个是类原型对象
    //第三个是装饰的属性,第四个是underfined
    var c = arguments.length, 
    // r=desc=undefined
        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--)
        // r=d(target, key, r) || r
            if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
     // 返回 r 
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};

其实最终就是执行下面函数

(function (target, propertyName) {
    target[propertyName] = propertyValue;
})(target, key);

和类装饰不一样一点,这里最后返回值是类函数

2.3 方法装饰器(访问器set.get也属于方法)

参数:

1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2. 属性的名称。
3.方法的属性描述符

function methodDecorator(params: string) {
    return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
        const method = descriptor.value;
        descriptor.value = function (...args: any[]) {
            //转换为字符串
            args = args.map(arg => String(arg));
            return method.apply(this, args)
        };
        return descriptor;
    }
}

class test {
    constructor() {
        console.log('构造函数')
    }

    @methodDecorator('测试方法装饰器')
    getAge(age: number) {
        console.log(age);
    }
}
let p = new test();
p.getAge(18);

下面是解析之后的代码:

var __decorate = (this && this.__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 methodDecorator(params) {
    return function (target, propertyName, descriptor) {
        var method = descriptor.value;
        descriptor.value = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            //转换为字符串
            args = args.map(function (arg) { return String(arg); });
            return method.apply(this, args);
        };
        return descriptor;
    };
}
var test = /** @class */ (function () {
    function test() {
        console.log('构造函数');
    }
    test.prototype.getAge = function (age) {
        console.log(age);
    };
    __decorate([
        methodDecorator('测试方法装饰器')
    ], test.prototype, "getAge", null);
    return test;
}());
var p = new test();
p.getAge(18);

这里__decorate调用的第4个参数不是undefined了而是null,还是看第一部分,Object.getOwnPropertyDescriptor方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)不懂的话可以点击这里

    //方法装饰器
        var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
        //c=4; r={writable: true, enumerable: true, configurable: true, value: f}(getAge 属性描述符对象)
            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 (target, propertyName, descriptor) {
        var method = descriptor.value;
        descriptor.value = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            //转换为字符串
            args = args.map(function (arg) { return String(arg); });
            return method.apply(this, args);
        };
        return descriptor;
    })(test.prototype, "getAge",{writable: true, enumerable: true, configurable: true, value: f})

这个函数里面其实是个闭包,把test.prototype.getAge的value作为method变量先保存起来,然后再改了value的值,当调用test.prototype.getAge方法的时候可以将调用的参数转变为字符串,然后再去执行之前保存好的method 方法!

2.4 参数装饰器

参数:

1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2. 属性的名称。
3.参数的index的值

function paramsDecorator(target: any, propertyName: string, index: number) {
    var key = propertyName + "index"
    target[key] = index;
}


class test {
    constructor() {
        console.log('构造函数')
    }

    public getAge(@paramsDecorator age: number) {
        console.log(age);
    }
}
let p = new test();
p.getAge(18);

下面是解析之后的代码:

var __decorate = (this && this.__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;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
function paramsDecorator(target, propertyName, index) {
    var key = propertyName + "index";
    target[key] = index;
}
var test = /** @class */ (function () {
    function test() {
        console.log('构造函数');
    }
    test.prototype.getAge = function (age) {
        console.log(age);
    };
    __decorate([
        __param(0, paramsDecorator)
    ], test.prototype, "getAge", null);
    return test;
}());
var p = new test();
p.getAge(18);

首先会先调用 __param(0, paramsDecorator),

var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
__param(0, paramsDecorator);

又是返回一个闭包函数,如下:

// paramIndex = 0;
function (target, key) { decorator(target, key, paramIndex)}

第一部分最后可以简化为

//r 为属性描述符对象, d为上面返回的闭包函数,target为类原型对象,key为属性名
r=d(target, key, r) || r
return r

最后执行上面的闭包函数

3.装饰器执行顺序

先看段代码:

function paramsDecorator(target: any, propertyName: string, index: number) {
    console.log("参数装饰器")
}

function methodDecorator(target: any, propertyName: string, descriptor: PropertyDescriptor) {
    console.log("方法装饰器")
}


function ClassDecoratorOne(target: any) {
    console.log('类装饰器');
}

function propertyDecorator(target: test, propertyName: string) {
    console.log("属性装饰器")
}


function enumerable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("访问器装饰器")

}


@ClassDecoratorOne
class test {
    constructor() {

    }

    @methodDecorator //方法装饰器
    //参数装饰器
    public getAge(@paramsDecorator  value: number) {

    }

    @propertyDecorator //属性装饰器
    public age: string
    public name: string

    @enumerable   //访问装饰器
    get getName() {
        return this.name;
    }
}
let p = new test();
p.getAge(18);

执行结果如下:
TypeScript装饰器原理分析_第1张图片
如果把方法,属性,访问器装饰器位置调整下,访问器在最上面,方法装饰器在中间,最后放属性装饰器呢,看下面代码:

function paramsDecorator(target: any, propertyName: string, index: number) {
    console.log("参数装饰器")
}

function paramsDecoratorConstructor(target: any, propertyName: string, index: number) {
    console.log("构造参数装饰器")
}

function methodDecorator(target: any, propertyName: string, descriptor: PropertyDescriptor) {
    console.log("方法装饰器")
}


function ClassDecoratorOne(target: any) {
    console.log('类装饰器');
}

function propertyDecorator(target: test, propertyName: string) {
    console.log("属性装饰器")
}


function enumerable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("访问器装饰器")

}


@ClassDecoratorOne
class test {

    constructor(@paramsDecoratorConstructor name:string) {
         this.name  = value
        console.log("构造函数")
    }   

    @enumerable   //访问装饰器
    get getName() {
        return this.name; 
    }

    @methodDecorator //方法装饰器
    //参数装饰器
    public getAge(@paramsDecorator value: number) {

    }

    @propertyDecorator //属性装饰器
    public age: string
    public name: string


}
let p = new test('张三');
p.getAge(18);

执行结果如下:
TypeScript装饰器原理分析_第2张图片

所以可以得出结论:不管怎么样,有参数的就先执行参数装饰器(类装饰器也如此,先执行类参数装饰器,再执行类装饰器),访问器,方法,属性装饰器,谁写在前面谁先执行,看代码的先后顺序,然后再执行构造函数参数装饰器(有的话),最后是类装饰器!

你可能感兴趣的:(typeScript,typescript,javascript)