前端练习47 自动绑定实例方法

已经同步到个人博客,欢迎访问

知识点

  1. class内部默认是严格模式
  2. class定义的原型方法的可枚举行
  3. . Proxy

题目

首先看这样一个引子:

const name = 'window';
let obj = {
  name: 'jay',
  say() {
    console.log(this.name)
  }
};

obj.say(); 

let say = obj.say;
say();

两次执行,分别会输出什么?

显然,第一次会打印出jay,第二次是window(,如果在严格模式下会报错),是因为this指向调用者,第二次的调用者是window

在构建类的时候,同样有这个问题:

class Person {
  constructor (name) {
    this.name = name
  }
  sayHi () {
    console.log(`I am ${this.name}.`)
  }
}

const jerry = new Person('Jerry')
const sayHi = jerry.sayHi
sayHi() // => 报错

注意,这里面是直接报错,因为this是指向了undefined而不是window,因为在类和模块的内部,默认就是严格模式

所以在类似于React.js的组件的事件监听当中我们总是需要手动地进行bind(this)操作。为了简化这样的操作,请你完成一个方法autoBind,它可以接受一个类作为参数,并且返回一个类。返回的类的实例和原来的类的实例功能上并无差别,只是新的类的实例所有方法都会自动 bind 到实例上。例如:

const BoundPerson = autoBind(Person)

const lucy = new BoundPerson('Lucy')
const sayHi = lucy.sayHi
sayHi() // => I am Lucy.

注意,如果autoBind以后给原来的类新增方法,也会自动反映在实例上,例如:

Person.prototype.sayGood = function () {
  console.log(`I am ${this.name}. I am good!`)
}

const sayGood = lucy.sayGood
sayGood() // => I am Lucy. I am good!

实现

一上来我就想到了可以使用Proxy来拦截new的操作符,后来又试了试用extends方法,都可以实现一半,但是后一半,也就是“自动反映在实力上”这个要求没办法实现

先讲一下我用Proxy的实现,利用handler.construct方法,拦截了new的操作

const autoBind = (ToBindClass) => new Proxy(ToBindClass, {
  construct(targets, argus, newTarget) {
    const self = new targets(...argus);

    Object.getOwnPropertyNames(targets.prototype).forEach(v => {
      self[v] = targets.prototype[v].bind(new targets(...argus))
    });

    return self
  }
})

在构造函数的方法中,使用了Object.getOwnPropertyNames(targets.prototype)对原型上的方法遍历进行绑定

要注意的是,class里面定义的原型方法是不可枚举的,这一点与直接定义在prototype是不同的,直接定义在prototype是可枚举的

所以遍历class的原型方法不能使用Objecet.keys方法,可以使用Object.getOwnPropertyNames

但是,这种方法没办法动对新增的方法进行处理。

看了看评论区,可以使用双层的Proxy实现,外层的Proxy不同,里面不直接返回实例,而是用Proxy再次进行处理的实例,对实例的get方法进行拦截,每次访问的时候再去原型链上找,然后进行绑定。

const autoBind = (ToBindClass) => new Proxy(ToBindClass, {
  construct(targets, argus, newTarget) {

    const self = new targets(...argus);

    return new Proxy(self, {
      get(target, key) {
        if(typeof target[key] === 'function') {
          target[key] = target[key].bind(self)
        }
        return target[key]
      }
    })
  }
})

外层的Proxy可以用函数自己去拦截构造器,所以上面也可以改写成:

const autoBind = (ToBindClass) => {
  return function(...argus) {
    const self = new ToBindClass(...argus);
    return new Proxy(self, {
      get(target, key) {
        if(typeof target[key] === 'function') {
          target[key] = target[key].bind(self)
        }
        return target[key]
      }
    })
  }
}

太笨了。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值