Webpack 配置
目前使用这个特性,需要使用webpack进行编译
首先安装插件:
npm install babel-plugin-transform-decorators-legacy --save-dev
然后在哎.babelrc中进行配置:
{
"presets": ["es2015", "stage-0", "react"],
"plugins": [
["transform-decorators-legacy"],
// ...
]
}
或者在webpack的配置文件中配置:
{
test: /\.jsx?$/,
loader: 'babel',
query: {
cacheDirectory: true,
plugins: ['transform-decorators-legacy' ],
presets: ['es2015', 'stage-0', 'react']
}
}
简介
许多面向对象的语言都有修饰器(Decorator)函数,用来修改类的行为。目前,有一个提案将这项功能,引入了 ECMAScript。
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
上面代码中,@testable
就是一个修饰器。它修改了MyTestableClass
这个类的行为,为它加上了静态属性isTestable
。testable
函数的参数target
是MyTestableClass
类本身。
基本上,修饰器的行为就是下面这样。
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
也就是说,修饰器是一个对类进行处理的函数。修饰器函数的第一个参数,就是所要修饰的目标类。
如果觉得一个参数不够用,可以在修饰器外面再封装一层函数。
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
注意,修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。
前面的例子是为类添加一个静态属性,如果想添加实例属性,可以通过目标类的prototype
对象操作。
function testable(target) {
target.prototype.isTestable = true;
}
@testable
class MyTestableClass {}
let obj = new MyTestableClass();
obj.isTestable // true
下面是另外一个例子。
// mixins.js
export function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list)
}
}
// main.js
import { mixins } from './mixins'
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // 'foo'
上面代码通过修饰器mixins
,把Foo
对象的方法添加到了MyClass
的实例上面。可以用Object.assign()
模拟这个功能。
const Foo = {
foo() { console.log('foo') }
};
class MyClass {}
Object.assign(MyClass.prototype, Foo);
let obj = new MyClass();
obj.foo() // 'foo'
实际开发中,React与Redux库结合使用时,常常需要写成下面这样。
class MyReactComponent extends React.Component {}
export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);
使用装饰器,可以写成下面:
@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}
方法的修饰
秀使其不仅可以修饰类,还可以修饰类的属性
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
修饰器函数readonly
一共可以接受三个参数。
function readonly(target, name, descriptor){
// descriptor对象原来的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable = false;
return descriptor;
}
readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);
- 修饰器第一个参数是类的原型对象,上例是
Person.prototype
,修饰器的本意是要“修饰”类的实例,但是这个时候实例还没生成,所以只能去修饰原型(这不同于类的修饰,那种情况时target
参数指的是类本身); - 第二个参数是所要修饰的属性名,
- 第三个参数是该属性的描述对象
descriptor
function decorator (target, name, descriptor){
descriptor.writable = false;
return descriptor
}
class Person {
constructor(name){
this.name = name
}
@decorator
say(){
alert('hello')
}
}
Person.prototype.say = function () {
console.log('1')
}
// Uncaught TypeError: Cannot assign to read only property 'say' of object '#<Person>'
如果同一个方法有多个修饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。
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
修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。
core-decorators.js
core-decorators.js是一个第三方模块,提供了几个常见的修饰器,通过它可以更好地理解修饰器。
@autobind
:使方法中的this
对象,绑定原始对象。@readonly
:使得属性或方法不可写。@override
:检查子类的方法,是否正确覆盖了父类的同名方法,如果不正确会报错。@deprecate
:在控制台显示一条警告,表示该方法将废除。
Mixin
在修饰器的基础上,可以实现Mixin模式。
function mixin(...argus){
return function(target){
return Object.assign(target, argus)
}
}
const foo = {name: 'jay'}
@mixin(foo)
class Person{
}
console.log(Person.name) // jay