最近学习了阮一峰老师讲解ES6的修饰器,感觉这个东西使用起来还是十分炫酷的,所以这里说一下修饰器的概念并自己实现一个修饰器。
1.平时使用的修饰器
介绍两个用的很频繁的修饰器,第一个呢是antd组件库中的Form组件,使用的时候我们要协商如下代码
class Discipline extends React.Component {
render(){}
}
export default Form.create()(Discipline)
第二个就是Redux中的connect函数,他使用的时候如下:
class Discipline extends React.Component {
function mapStateToProps(state) {
return state
}
function mapDispatchToProps(dispatch) {
return {
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Discipline)
看过了装饰器之后我们知道他可以写成@xxx的形式,这两个装饰器都是作用于类上面的,所以可以写成如下形式
@connect((store) => {
return { store };
}, (dispatch) => {
return { dispatch };
})
class Discipline extends React.Component {
render(){}
}
export default Discipline
如果你看过compose这个函数,并且你的页面既需要connect又需要form,你就可以这个样子写
@compose(
Form.create(),
connect(
state => {
return state;
},
dispatch => {
return dispatch
},
)
)
class Discipline extends React.Component {
render(){}
}
export default Discipline
2.对于修饰器我的一些理解
对于类方法装饰器而言(这里强调类方法,是因为普通方法存在变量提升无法使用装饰器),装饰器本质是作为一个方法存在的,这个方法内部会以一种新的方式去执行原有的方法,进而对被装饰的方法做一些限制和操作。
比如这个打印日志的方法:
function log(target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = function() {
console.log(`Calling ${name} with`, arguments);
return oldValue.apply(this, arguments);
};
return descriptor;
}
简单介绍一下三个参数,第一个呢是类的原型,具体表现为Class.prototype
,第二个参数是要修饰的属性名,即被修饰的方法的名称,第三个参数是该属性的描述对象,该属性的一些行为都是根据描述对象来定义的,有默认行为,也可以进行修改,稍微了解下就可以,想深入的话推荐《你不知道的JavaScript 上卷》。
log函数修改了原方法的描述符,调用原方法会触发value从而打印日志。为了保证不影响原方法,这里用到了oldValue作为中间变量,在新的value方法中执行一下之前的value方法。
3.自己实现一个修饰器
就简单实现一个自动绑定this的修饰器。
function autoBind(target, name, descriptor) {
if (!name) {
throw new Error('this decorator must be used for class property');
} //必须用于类属性,如果作用于class或其他不会有name参数
var oldValue = descriptor.value;
descriptor.value = function() {
if(target.isPrototypeOf(this)){
if(!this.hasOwnProperty('constructor')) {
return oldValue.bind(this)()
}
}
throw new Error('the symbol is not a instance')
}
return descriptor
}
这里有两个判断,首先确定我们要绑定的this指向的是当前的Class,想象一个场景,B继承了A的方法,你在B中调用该方法,A.prototype.fun()
,此时target指向A.prototype
,但是this指向B的实例。这样子的调用是不允许的。
第二个判断,情况同上,你调用该方法B.prototype.foo()
,这种调用方式也是不被允许的,通过判断过滤掉这两种不支持的方法调用。
以上就是本次的所有内容了,如有问题,请留言。