装饰器的常见作用
1.装饰一个类的属性
2.装饰一个类
装饰器只能针对类和类的属性,不能直接作用于函数,因为存在函数提升。
装饰类的属性
function readonly(target, name, descriptor) {
discriptor.writable = false;
return discriptor;
}
class Cat {
@readonly
say() {
console.log("meow ~");
}
}
var kitty = new Cat();
kitty.say = function() {
console.log("woof !");
}
kitty.say() // meow ~
ES6中的类实际上就是一个语法糖,本质上是构造函数,类的属性的定义使用的是 Object.defineProperty()
类的代码可以改写为下面代码:
class Cat {
say() {
console.log("meow ~");
}
}
// 上面等价于下面代码
function Cat() {}
Object.defineProperty(Cat.prototype, "say", {
value: function() { console.log("meow ~"); },
enumerable: false,
configurable: true,
writable: true
});
在say
方法前面加入@readonly后,整个代码等价于下面代码:
function readonly(target, name, descriptor) {
discriptor.writable = false;
return discriptor;
}
function Cat() {}
let descriptor = {
value: function() { console.log("meow ~"); },
enumerable: false,
configurable: true,
writable: true
};
descriptor = readonly(Cat.prototype, 'say', descriptor) || descriptor;
Object.defineProperty(Cat.prototype, "say", descriptor);
装饰类
装饰一个类的时候类本身本质上是一个函数,没有descriptor,target是这个函数本身。
function isAnimal(target) {
target.isAnimal = true;
return target;
}
@isAnimal
class Cat {
...
}
console.log(Cat.isAnimal); // true
也就是说,上面的@isAnimal
其实就是做了下面这件事
Cat = isAnimal(function Cat() { ... });
注意
- 作用在方法上的
decorator
接收的第一个参数(target )是类的prototype
;如果把一个decorator
作用到类上,则它的第一个参数target
是 类本身。 - 装饰器执行的时间是在属性定义的时候,也就是被装饰的属性在定义后就是已经被装饰器处理过的不一样的属性了。
装饰器的执行时机
function log(message) {
return function() {
console.log(message);
}
}
console.log('before class');
@log('class Bar')
class Bar {
@log('class method bar');
bar() {}
@log('class getter alice');
get alice() {}
@log('class property bob');
bob = 1;
}
console.log('after class');
let bar = {
@log('object method bar')
bar() {}
};
输出结果:
before class
class method bar
class getter alice
class property bob
class Bar
after class
object method bar
可以看出装饰器在类和类的属性定义的时候就对它们进行了"装饰"。