初探bind

293 阅读3分钟

初探bind

根据MDN的介绍:

bind()方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。 个人理解:

  • bind()方法用在函数上,并实现this指向的转移,转移到bind()的第一个参数
  • 除了第一个参数指定this外,其后可以传入若干个参数,做为原函数的参数

bind()的简单使用

const Module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};

const unboundGetX = Module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

const boundGetX = unboundGetX.bind(Module);
console.log(boundGetX());
// expected output: 42

bind()传参用法

先解释一个名词:偏函数

拥有预设的初始参数的函数,比如想实现计算1 + x的函数add(),其中x是未知数,并由用户输入,其中的1是固定的,不过也是用户之前传入的,之后想计算任何1 + x只需要调用add(x),而不用调用add(1,x)

bind()可以实现偏函数的功能,代码如下:

// 将arguments转换成数组的函数
function list() {
  return Array.prototype.slice.call(arguments);
}
// 实现两个数的相加的函数
function addArguments(arg1, arg2) {
    return arg1 + arg2
}

// 创建一个函数,它拥有预设参数列表,相当于偏函数
var leadingThirtysevenList = list.bind(null, 37);

// 创建一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37); 

var list2 = leadingThirtysevenList(); 
// [37]

var list3 = leadingThirtysevenList(1, 2, 3); 
// [37, 1, 2, 3]

var result2 = addThirtySeven(5); 
// 37 + 5 = 42 

var result3 = addThirtySeven(5, 10);
// 37 + 5 = 42 ,第二个参数被忽略

配合setTimeout

读者只需要了解在setTimeout中申明的函数this指向window,为了使预想功能实现,可以通过bind()显示指定函数的指向。

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

LateBloomer.prototype.bloom = function() {
// 如果没用bind(),declare中的this将会是window,则会与预想不一致
  window.setTimeout(this.declare.bind(this), 1000);
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用 'declare' 方法

手写最基本的bind()

我们要考虑若干问题

  • bind()是属于谁的方法?Function.prototype
  • 谁可以调用bind()?任意Function
  • 当使用bind时,this指向谁?函数的调用者 我们还是那第一段代码来举一个栗子吧
const Module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};

const unboundGetX = Module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

const boundGetX = unboundGetX.bind(Module);
console.log(boundGetX());
// expected output: 42

unboundGetX调用了bind(),unboundGetX是一Module中的一个函数,函数可以调用bind(),因为原型中有bind(),但此时的unboundGetX已经脱离了Module,是一个位于global中的函数,也就是window的函数,因此此时的unboundGetX中的this指向window,我们现在调用这个函数,返回了什么给boundGetX呢?对的,返回了一个函数,因为下文调用了该函数,此时返回的其实是一个全新的函数,this指向了Module,让我们误以为我们改变了unboundGetXthis的指向,其实没有,只不过返回了一个新函数给我们,现在我们只要考虑如何在bind()函数中构造一个指向Module的函数并返回即可。

// 如果
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(){
    //这里的arguments和上面的arguments不同,是属于调用bind()绑定后的,而上方是绑定时传入的参数
    //这也就是可以实现偏函数的原因了,利用concat将新参数接到前面的参数后面
      var funcArgs = args.concat(slice.call(arguments))
      return thatFunc.apply(thatArg, funcArgs);
    };
  };
})();

更多

名词细节请参照关于bind的MDN文档

手写高级bind()建议查看:大神冴羽的bind()教程

本孩水平有限,bind()的再探得等我长大点。本文便于对初学者理解bind(),收藏价值有限,如果想表扬孩子的话,点赞吧!!!!哈哈哈哈哈哈,不点赞我要哭了。。。。。。