ES6 - 手动实现一个自动绑定this的修饰器

最近学习了阮一峰老师讲解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(),这种调用方式也是不被允许的,通过判断过滤掉这两种不支持的方法调用。

以上就是本次的所有内容了,如有问题,请留言。

猜你喜欢

转载自blog.csdn.net/Run_youngman/article/details/80596882