decorator 是装饰者,是 ES7 的语法。
decorator 本质是一个 wrapper,可以动态增强【类】,【实例方法】的能力
被装饰者对于装饰者是毫无感知的
读者如果感兴趣,可以深入学习【装饰者模式】
要注意的是,在 ES7 中,decorator 只是一个语法糖而已,和 class 这些一样
可以参考 babel 转换之后的 ES5 代码
decorator 可以在定义类的时候,作用在类方法上,见下面的例子:
function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
class People {
@readonly
sayName() {
console.log(this.name);
}
}
var p = new People();
p.sayName = 'hello';
执行上面的代码,会报错,如下:
decorator 本质是一个 wrapper 函数,当 decorator 作用在类方法的时候
它的签名是function(target, key, descriptor)
People.prototype
sayName
可以发现,decorator 的签名和 Object.defineProperty
的签名是一样的
本质上来说,作用在类方法的 decorator 就是对 Object.defineProperty
的 wrapper
decorator 可以在定义类的时候,作用在类上面,对类进行增强
function nonTestable(target) {
target.canTest = false;
}
@nonTestable
class People {}
console.log(People.canTest); // false
当 decorator 作用在类上时,只会传入一个参数,就是类本身
当同一个 decorator 在作用在不同目标上面时,有不同的表现时,可以把 decorator 定义为一个工厂函数,详见下面的例子:
function addName(name) {
return function(target) {
target.myName = name;
};
}
@addName('people')
class People {
static say() {
console.log(People.myName);
}
}
@addName('dog')
class Dog {
static say() {
console.log(Dog.myName);
}
}
People.say(); // people
Dog.say(); // dog
既然是 ES7 的新语法,那么如何使用 decorator?
目前需要使用 babel 来进行转换才能使用,具体方法如下:
.babelrc
文件如下:
{
"presets": ["es2015", "stage-1"],
"plugins": [
"babel-plugin-transform-decorators-legacy"
]
}
然后需要安装插件:
npm i babel-cli babel-preset-es2015 babel-preset-stage-1 babel-plugin-transform-decorators
安装完成之后,就可以使用 babel 来转换带 decorator 的代码了:
babel index.js > index.es5.js
实际上,decorator 有非常广泛的运用场景,这里我简单举两个例子
废话少说,直接举例子:
function nameMixin(target) {
target.prototype.setName = function(name) {
this.name = name;
return this;
};
target.prototype.sayName = function() {
console.log(this.name);
};
}
function ageMixin(target) {
target.prototype.setAge = function(age) {
this.age = age;
return this;
};
target.prototype.sayAge = function() {
console.log(this.age);
};
}
@nameMixin
@ageMixin
class People {}
var p = new People();
p.setName('peter').sayName(); // peter
p.setAge(18).sayAge(); // 18
可以对比一下以前的 mixin 使用方式:
class People extends mixin(ageMixin, nameMixin) {}
// or
class People {}
mixin(People, ageMixin, nameMixin);
可以看到,使用 decorator 之后,代码简单明了了许多
function aop(before, after) {
return function(target, key, descriptor) {
const method = descriptor.value;
descriptor.value = (...args) => {
let ret;
before && before(...args);
ret = method.apply(target, args);
if (after) {
ret = after(ret);
}
return ret;
};
};
}
function beforeTest1(opt) {
opt.name = opt.name + ', haha';
}
function beforeTest2(...args) {}
function afterTest2(ret) {
console.log('haha, add 10!');
return ret + 10;
}
class Test {
@aop(beforeTest1)
static test1(opt) {
console.log(`hello, ${opt.name}`);
}
@aop(beforeTest2, afterTest2)
static test2(...args) {
return args.reduce((a, b) => a + b);
}
}
/**
* out:
*
* hello, peter, haha
* haha, add 10!
* 16
*
*/
Test.test1({
name: 'peter'
});
console.log(Test.test2(1, 2, 3));
这里只是实现了一个简单的 aop 例子
大家可以看到,使用了 decorator 之后,代码非常清晰
decorator 是 ES7 的新语法,本质上来说,它就是一个语法糖,但是缺非常有用
任何装饰者模式
的代码都可以通过 decorator 来实现
使用了 decorator 之后,代码会变得清晰明了
相信不久的将来,ES7 语法将会流行,但是在这之前,你仍然可以通过 babel 来使用 decorator
快来享受 decorator 带来的简洁吧!