说说bind、call、apply的区别?并手写实现一个bind的方法

bindcallapply 都是 JavaScript 中用于改变函数执行上下文(即函数内部的 this 指向)的方法,它们的主要区别在于:

  • call: 立即调用函数,并接受参数列表作为后续参数。
  • apply: 立即调用函数,并接受一个参数数组作为参数。
  • bind: 创建一个新的函数,该函数的 this 值被绑定到指定的值,并可以预先设置一些参数。它不会立即执行原函数,而是返回一个新的函数,可以在之后调用。

更详细的解释:

  • call(thisArg, arg1, arg2, ...): 第一个参数 thisArg 是要绑定到函数的 this 值。后续参数 arg1, arg2 等是传递给函数的参数。

  • apply(thisArg, [arg1, arg2, ...]): 第一个参数 thisArgcall 一样。第二个参数是一个数组(或类数组对象),包含传递给函数的参数。

  • bind(thisArg, arg1, arg2, ...): 第一个参数 thisArgcallapply。后续参数 arg1, arg2 等是预先设置给函数的参数。bind 返回一个新的函数,当这个新函数被调用时,预先设置的参数会先于新函数调用时传入的参数传递给原函数。

手写实现 bind 方法:

Function.prototype.myBind = function(thisArg, ...boundArgs) {
  const originalFunction = this; // 保存原函数

  // 返回一个新函数
  return function(...args) {
    // 合并预设参数和调用时传入的参数
    const allArgs = boundArgs.concat(args);

    // 使用 apply 调用原函数,并设置 this 值和参数
    return originalFunction.apply(thisArg, allArgs);
  };
};


// 测试用例:
function greet(greeting, name) {
  console.log(`${greeting}, ${name}! I am ${this.title}`);
}

const person = {
  title: "Professor"
};

const greetPerson = greet.myBind(person, "Hello");
greetPerson("John"); // 输出: Hello, John! I am Professor

const greetPerson2 = greet.myBind(person, "Hi", "Jane");
greetPerson2(); // 输出: Hi, Jane! I am Professor

const greetPerson3 = greet.myBind(person);
greetPerson3("Good morning", "David"); // 输出: Good morning, David! I am Professor


关键点解释:

  1. 保存原函数: const originalFunction = this; 保存对原函数的引用,以便在新函数内部调用。

  2. 返回新函数: bind 的核心是返回一个新函数,这个新函数会在之后被调用。

  3. 合并参数: boundArgs.concat(args) 将预设参数 boundArgs 和调用新函数时传入的参数 args 合并成一个数组 allArgs

  4. 使用 apply 调用原函数: originalFunction.apply(thisArg, allArgs) 使用 apply 方法调用原函数,并将 this 值设置为 thisArg,参数设置为合并后的参数数组 allArgs

这个手写实现涵盖了 bind 的核心功能。一些更完善的实现还会处理一些边缘情况,例如:

  • new 操作符: 当使用 new 操作符调用绑定函数时,绑定的 this 值会被忽略。
  • 返回函数的 prototype: 需要正确设置返回函数的 prototype 属性。

这个更完善的版本,可以参考MDN的polyfill:

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to throwing a TypeError in IE6-8
      // since it doesn't support accessing the 'caller' property
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis || this,
posted @   王铁柱6  阅读(46)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示