bind

77 阅读2分钟

描述

bind() 函数会创建一个新的绑定函数bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数
绑定函数具有以下内部属性:

  • [[BoundTargetFunction]] - 包装的函数对象
  • [[BoundThis]] - 在调用包装函数时始终作为 this 值传递的值。
  • [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
  • [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。

当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(boundThisargs)。其中,boundThis 是 [[BoundThis]]args 是 [[BoundArguments]] 加上通过函数调用传入的参数列表。

绑定函数也可以使用 new 运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的 this 值会被忽略,但前置参数仍会提供给模拟函数。

实现

 
// fn.bind(content) bind 返回函数
Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  const args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
	// 此时的this 是new 出来的obj,跟context 没有关系, 参照下面的 demo
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

Function.prototype.bind = function(thisObj) {
  // 判断调用 bind 的方法是不是一个函数
  if(typeof this !== 'function') {
    throw new TypeError('只能对函数使用 bind 方法')
  }

  // 保存当前函数的指针
  const self = this;
  // 拿到除第一个参数以外的其他参数列表
  const args = [].slice.call(arguments,1)

  function Bound() {
    // 如果 this instanceof Bound,说明是通过 new 关键字调用
    // 这时就将 this 直接绑定到 self,而忽略 thisObj,在 self 内部会对 this 添加属性和方法
    return self.apply(this instanceof Bound ? this : thisObj,args.concat([].slice.apply(arguments)))
  }

  // 实现继承
  if(this.prototype){
    Bound.prototype = Object.create(this.prototype)
  }

  return Bound
}

// call apply 判断是否为对象 bind判断对否为方法

demo

let value = 2;
let foo = {
    value: 1
};
function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}
bar.prototype.friend = 'kevin';

let bindFoo = bar.bind(foo, 'Jack');
let obj = new bindFoo(20);
// undefined
// Jack
// 20

obj.habit;
// shopping

obj.friend;
// kevin

// 上面例子中,运行结果 `this.value` 输出为 `undefined` ,这不是全局 `value` 也不是 `foo` 对象中的 `value` ,
// 这说明 `bind` 的 `this` 对象失效了,`new` 的实现中生成一个新的对象,这个时候的 `this` 指向的是 `obj` 。