TypeScript装饰器装饰器是一种特殊类型的声明,它能够被附加到类声明,属性, 访问符,方法或方法参数
上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
参数:
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); //你把整个类都改了,打印出来是"我是装饰器的返回值"
参数:
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);
和类装饰不一样一点,这里最后返回值是类函数
参数:
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 方法!
参数:
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
最后执行上面的闭包函数
先看段代码:
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);
执行结果如下:
如果把方法,属性,访问器装饰器位置调整下,访问器在最上面,方法装饰器在中间,最后放属性装饰器呢,看下面代码:
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);
所以可以得出结论:不管怎么样,有参数的就先执行参数装饰器(类装饰器也如此,先执行类参数装饰器,再执行类装饰器),访问器,方法,属性装饰器,谁写在前面谁先执行,看代码的先后顺序,然后再执行构造函数参数装饰器(有的话),最后是类装饰器!