# js装饰器
# 方法上的decorator
先来看一个简单的类:
class Dog {
bark () {
return 'wang!wang!'
}
}
如果想让 bark 这个方法成为一个只读的属性,那么可以定义一个 readonly 的 decorator:
// 注意这里的target是Dog.prototype
function readonly(target, key, descriptor) {
descriptor.writable = false
return descriptor
}
可以看到,decorator 就是一个普通函数,只不过它接收 3 个参数,与 Object.defineProperty 一致。就是把 descriptor 的 writable 字段设为 false 。
然后把 readonly 作用到 bark 方法上:
class Dog {
@readonly
bark () {
return 'wang!wang!'
}
}
let dog = new Dog()
dog.bark = 'bark!bark!'
// Cannot assign to read only property 'bark' of [object Object]
@readonly 具体做了什么呢?即 Dog 这个 class 等价于:
// 步骤 1
function Dog () {}
// 步骤 2
Object.defineProperty(Dog.prototype, 'bark', {
value: function () { return 'wang!wang!' },
enumerable: false,
configurable: true,
writable: true
})
对 bark 方法应用 @readonly 之后,JS 引擎就会在进行步骤二之前调用这个 decorator:
let descriptor = {
value: function () { return 'wang!wang!' },
enumerable: false,
configurable: true,
writable: true
}
// decorator 接收的参数与 Object.defineProperty 一致
descriptor = readonly(Dog.prototype, 'bark', descriptor) || descriptor
Object.defineProperty(Dog.prototype, 'bark', descriptor)
所以,ES7 的 decorator,作用就是 返回一个新的 descriptor,并把这个新返回的 descriptor 应用到目标方法上。decorator 并非只能作用到类的方法/属性上,它还可以作用到类本身 。
# 类上的 decorator
作用在方法上的 decorator 接收的第一个参数( target )是类的 prototype;如果把一个 decorator 作用到类上,则它的第一个参数 target 是类本身:
// 这里的 `target` 是类本身
function doge (target) {
target.isDoge = true
}
@doge
class Dog {}
console.log(Dog.isDoge)
// true
# decorator 也可以是 factory function
如果想对不同的目标对象应用同一个 decorator,但同时又需要有一些差别,怎么办?很简单:
function doge (isDoge) {
return function(target) {
target.isDoge = isDoge
}
}
@doge(true)
class Dog {}
console.log(Dog.isDoge)
// true
@doge(false)
class Human {}
console.log(Human.isDoge)
// false
对方法来说也是类似的:
function enumerable (isEnumerable) {
return function(target, key, descriptor) {
descriptor.enumerable = isEnumerable
}
}
class Dog {
@enumerable(false)
eat () { }
}
多个装饰器时,类似洋葱圈模式,先从外到内,再有内到外
function dec(id){
console.log('evaluated', id);
return (target, property, descriptor) =>console.log('executed', id);
}
class Example {
@dec(1)
@dec(2)
method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1
应用场景:react-redux的connect,类的混入某些新属性或者修改属性
目前如果想要使用,要在babel中配置。