bind原理及模拟实现

前言

如果不了解bind的使用的请前往 Function.prototype.bind,这里不在赘述。

想要看bind原理的童鞋,需要对以下知识有了解:

思路

  1. bind方法不会立即执行函数,而且需要保存一个入参,所以这里利用到了 闭包
  2. 改变函数的作用域,因此要利用 apply 或者 call方法来实现
  3. 获取函数的所有入参arguments,以及使用 slice 转化出一个数组

先利用上面的思路实现一个大致雏形的实现代码:

Function.prototype.mybind = function () {
  if (typeof this !== 'function') throw 'Bind must be called on a function'
  let _this = this, //这里的this是原函数
    context = arguments[0],//获取要this指向的对象
    slice = Array.prototype.slice,
    args = slice.call(arguments, 1);//获取bind函数除this指向对象外的所有参数
  //返回函数    
  return function () {
    args = args.concat(slice.call(arguments))//合并bind的入参和执行时的入参
    return _this.apply(context, args)
  }
}

此时我们可以测试一下:

function test(a, b) {
  console.log('this.name', this.name)
  console.log('a', a)
  console.log('b', b)
  return str
}
const henry = {
  name: 'henry',
}
// test.bind(henry, 1)(2, 3)
test.mybind(henry, 1)(2, 3)

好了,到此我们实现了bind的基础用法,但是还没完,假如我们用bind返回的函数当构造函数new一个对象会怎样呢?

原型链

官方文档 上有一段话:

A bound function may also be constructed using the new operator: doing so acts as though the target function had instead been constructed. The provided this value is ignored, while prepended arguments are provided to the emulated function.

意思就是说绑定函数也可以使用new操作符创建实例,这样做的行为就像是构造了目标函数一样,提供的this将被忽略,而前置参数还会参与使用。

  1. 我们要让实例的构造函数是目标函数
  2. 检测绑定函数的this是否是目标函数的实例,是的话则忽略绑定时提供的this
...
context = this instanceof _this ? this : context
...
fn.prototype = _this.prototype

完整的代码是:

Function.prototype.mybind = function () {
  if (typeof this !== 'function') throw 'Bind must be called on a function'
  let _this = this,
    context = arguments[0],//获取要this指向的对象
    slice = Array.prototype.slice,
    args = slice.call(arguments, 1);//获取bind函数除this指向对象外的所有参数
  //返回函数    
  const fn = function () {
    args = args.concat(slice.call(arguments))//合并bind的入参和执行时的入参
    context = this instanceof _this ? this : context //如果this指向的对象的构造函数是原函数,则说明this是new过程中产生的对象,此时忽略绑定时提供的context
    return _this.apply(context, args)
  }
  fn.prototype = _this.prototype
  return fn
}

测试一下:

function test() {}
const testBind = test.mybind()
const tb = new testBind()
console.log('tb', tb.constructor) //tb ƒ test() {}

好像到这来都没问题了,那我们来看看下面代码执行的结果

function test() { }
const testBind = test.mybind()
const tb = new testBind()
testBind.prototype.say = function () {
  console.log('say testBind')
}
test.prototype.say = function () {
  console.log('say test')
}
tb.say() //say test

这不符合bind使用new的结果,结果应该是say testBind才对,这是因为绑定函数和构造函数的原型对象是同一个,所以我们这里应该用Object.create优化一下,完整代码如下:

Function.prototype.mybind = function () {
  if (typeof this !== 'function') throw 'Bind must be called on a function'
  let _this = this,
    context = arguments[0],//获取要this指向的对象
    slice = Array.prototype.slice,
    args = slice.call(arguments, 1);//获取bind函数除this指向对象外的所有参数
  //返回函数    
  const fn = function () {
    args = args.concat(slice.call(arguments))//合并bind的入参和执行时的入参
    context = this instanceof _this ? this : context //如果this指向的对象的构造函数是原函数,则说明this是new过程中产生的对象,此时忽略绑定时提供的context
    return _this.apply(context, args)
  }
  fn.prototype = Object.create(_this.prototype)
  return fn
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值