ES6 Decorator 装饰器
理解 ES6 装饰器的概念、用法和实际应用场景
问题
如何理解 ES6 中的 Decorator?有哪些使用场景?
解答
什么是装饰器
装饰器(Decorator)是一个普通函数,用于在不改变原类和使用继承的情况下,动态扩展类属性和类方法。
举个例子,定义一个士兵类和装备装饰器:
// 士兵类
class Soldier {}
// 装饰器函数
function strong(target) {
target.hasAK = true;
}
// 使用装饰器
@strong
class Soldier {}
Soldier.hasAK // true
类的装饰
类装饰器接收一个参数:类本身。
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
为类添加静态属性:
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true;
}
传递参数需要在外层再封装一层函数:
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
类属性的装饰
类属性装饰器接收三个参数:
- 类的原型对象
- 需要装饰的属性名
- 装饰属性名的描述对象
实现一个 readonly 装饰器:
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
// 等同于
readonly(Person.prototype, 'name', descriptor);
多个装饰器的执行顺序
多个装饰器像洋葱一样,先从外到内进入,再由内到外执行:
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
注意事项
装饰器不能用于修饰函数,因为函数存在变量提升:
var counter = 0;
var add = function() {
counter++;
};
@add
function foo() {}
// 编译后变成
var counter;
var add;
@add
function foo() {}
counter // 0,而不是预期的 1
实际应用场景
简化 React Redux 连接:
// 传统写法
class MyReactComponent extends React.Component {}
export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);
// 使用装饰器
@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}
实现 mixins:
function mixins(...list) {
return function(target) {
Object.assign(target.prototype, ...list);
};
}
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // "foo"
使用 core-decorators.js 库:
import { autobind, readonly, deprecate } from 'core-decorators';
class Person {
// 绑定 this
@autobind
getPerson() {
return this;
}
// 只读属性
@readonly
name = 'John';
// 废弃警告
@deprecate('该方法将废除')
oldMethod() {}
}
关键点
- 装饰器是一个函数,用于扩展类和类方法,不改变原有代码
- 类装饰器接收类本身作为参数,类属性装饰器接收原型对象、属性名和描述对象
- 多个装饰器执行顺序:外层先进入,内层先执行
- 装饰器不能用于普通函数,因为存在变量提升问题
- 常用于简化代码(如 Redux 连接)、mixins、方法增强(autobind、readonly)等场景
目录