【javascript】关于bind的实现

前言

  • 听说bind有点特别啊,一开始初学不知道,以为那么简单的东西就绑成函数等待执行呗,后来发现这玩意实际还是比较复杂的。

mdn上Polyfill

  • 先看mdn上Polyfill
// Does not work with `new funcA.bind(thisArg, args)`
if (!Function.prototype.bind) (function(){
  var slice = Array.prototype.slice;
  Function.prototype.bind = function() {
    var thatFunc = this, thatArg = arguments[0];
    var args = slice.call(arguments, 1);
    if (typeof thatFunc !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - ' +
             'what is trying to be bound is not callable');
    }
    return function(){
      var funcArgs = args.concat(slice.call(arguments))
      return thatFunc.apply(thatArg, funcArgs);
    };
  };
})();
  • 前面有个注释,这个不支持在用new来生成绑定函数。也就是说,原版的bind可以new的。
  • 当然原版还有很多特别的地方。写几个小例子测试下原版。

原版测试

  • 简单情况:
function aaa() { }
ccc = aaa.bind('ddd')
console.log(ccc.__proto__)
console.log(ccc.constructor)
console.log(ccc.prototype)
  • 这个就是只有一个函数然后直接bind情况。这3个打印分别是Function.prototype,Function,undefined。
  • 就是这个undefined表示了函数生成的bind函数,无prototype。
  • 这好像暗示我们bind内部用箭头函数实现,但是,箭头函数不能new啊。
  • 使用传参形式结合new看看效果:
function aaa(x,y) {
    this.x=x
    this.y=y
}
aaa.prototype.getXY=function(){
    return this.x+this.y
}
let bindfunc = aaa.bind(null,11)
let ins = new bindfunc(33)
console.log(ins.getXY())//表示实例能拿原函数的prototype
console.log(ins instanceof bindfunc)//true
console.log(ins instanceof aaa)//true
console.log(ins.constructor)//aaa
console.log(bindfunc.constructor)//同第一种情况
console.log(bindfunc.__proto__)//同上
console.log(bindfunc.prototype)//同上
  • 通过console其实可以推断,bind所返回的函数的prototype,应该是能通过原型链找到原函数的prototype的。而且如果new的话,this不是指向绑定this,而是指向实例本身(这个就有点跟bind设计的意思冲突,用bind想改变this,结果new的时候this又不改变)。
  • 正常的函数,new出来实例的constructor会指向这个函数。所以正常来说,这个Ins的constructor应该指向bindfunc才对。所以bindfunc的prototype被调换过了。
  • 看一下mdn推荐的大神写的bind打印结果也和真正的bind不一样。大神写的bind地址
  • 用上面那个例子测试大神写的这个bind,会发现打印结果:
44
true
true
[Function: aaa]
[Function: Function]
[Function] { mybind: [Function: mybind] }
aaa {}
  • 大神除了undefined问题,其他基本上都跟原版打印的一样。

实现bind

  • 对于这种问题,最好就是画个图,然后照着图连就行了。这里我画了个图:
    在这里插入图片描述
  • 可以发现,如果用Js实现bind,undefined问题是无法避免的,因为你能new,而且必须让实例找到原函数上,就得设置bindfunc的prototype。不然你new出来的实例找不到路。虽然js可以通过给bindfunc设置代理,让其prototype的打印值为undefined,但是这样你返回的函数就是代理对象,而不是原来那个绑定函数。
  • 我们可以通过instanceof 判断是不是被new调用,如果是new 调用,那么外面那个this是原函数,里面那个this是new出来的对象。
  • 最后可以把前面大神写的简略点,改成这样:
Function.prototype.mybind = function (context, ...args) {
    let fn = this
    if (typeof fn !== 'function') {
        throw 'error'
    }
    let fbound = function (...innerArgs) {
        return fn.apply(
            this instanceof fn ? this : context, [...args, ...innerArgs]
        )
    }
    fbound.prototype = Object.create(fn.prototype)
    return fbound
}


function aaa(x, y) {
    this.x = x
    this.y = y
}
aaa.prototype.getXY = function () {
    return this.x + this.y
}
let bindfunc = aaa.mybind(null, 11)
let ins = new bindfunc(33)
console.log(ins.getXY())//表示实例能拿原函数的prototype
console.log(ins instanceof bindfunc)//true
console.log(ins instanceof aaa)//true
console.log(ins.constructor)
console.log(bindfunc.constructor)
console.log(bindfunc.__proto__)
console.log(bindfunc.prototype)
  • 这样基本上就差不多了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值