什么是装饰器
装饰器(Decorator)函数,是用来修改类的行为,一个装饰器函数有三个参数
target
装饰的目标对象name
类的成员,属性或者方法descriptor
成员描述,传递给Object.defineProperty
的描述
如何使用装饰器
目前浏览器还不支持装饰器,所以需要babel来进行转换,先全局安装 @babel/core
和 @babel/cli
npm i @babel/core @babel/cli -g
然后再局部安装 @babel/plugin-proposal-class-properties
和 @babel/plugin-proposal-decorators
npm i @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators -D
在.babelrc
配置babel插件
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
声明装饰器
我们都知道装饰器其实就是一个函数,所以声明一个装饰器很简单
// 声明一个只读的装饰器函数,通过descriptor给name改为只读
function readOnly(target, name, descriptor) {
descriptor.writable = false;
return descriptor
}
一个完整的栗子
// 声明一个只读的装饰器函数,通过descriptor给name改为只读
function readOnly(target, name, descriptor) {
descriptor.writable = false;
return descriptor
}
// 函数弃用
function deprecate(target, name, descriptor) {
return {
...descriptor,
value: function depreactionWrapper() {
console.warn('该函数将被移除');
return descriptor.value.apply(this, arguments)
}
}
}
class Person {
@readOnly
name = 'hello'
@readOnly
@deprecate
say() {
console.log(`my name is ${ this.name }`)
}
}
const p = new Person()
p.hello = 'fuck' // hello
p.say() // 该函数将被移除 my name is hello
通过命令行 babel index.js -o bundle.js
可以把 @decoratorFunction
语法转为es5就可以在浏览器执行,转换后的代码如下
var _class, _descriptor, _temp;
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
desc = null;
}
return desc;
}
function readOnly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
function deprecate(target, name, descriptor) {
return {
...descriptor,
value: function depreactionWrapper() {
console.warn('该函数将被移除');
return descriptor.value.apply(this, arguments);
}
};
}
let Person = (_class = (_temp = class Person {
constructor() {
_initializerDefineProperty(this, "name", _descriptor, this);
}
say() {
console.log(`my name is ${this.name}`);
}
}, _temp), (_descriptor = _applyDecoratedDescriptor(_class.prototype, "name", [readOnly], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 'hello';
}
}), _applyDecoratedDescriptor(_class.prototype, "say", [readOnly, deprecate], Object.getOwnPropertyDescriptor(_class.prototype, "say"), _class.prototype)), _class);
const p = new Person();
p.say();
可以看出装饰器就是通过 Object.defineProperty
在类的原型上的新增或者修改属性或者方法
装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
装饰器在react的使用
在使用react-redux的时候我们会用connect
的方法把store和当前组件关联,我们都知道connect
是一个高阶函数
import { connect } from 'react-redux';
class Post extends PureComponent{}
export default connect(mapStateToProps, mapDispatchToProps)(Post)
使用装饰器来写,就会变成下面这样
import { connect } from 'react-redux';
// connect store to component
@connect(mapStateToProps, mapDispatchToProps)
class Post extends PureComponent{}
export default Post