返回

函数原型方法call/apply/bind源码实现剖析

前端

概述

在 JavaScript 中,函数是第一等公民,这意味着函数可以被存储在变量中、传递给其他函数作为参数,甚至可以作为返回值。函数的 this 指向当前函数的调用对象,通常是全局对象或调用函数的对象。然而,有时候我们需要改变函数的 this 指向,以便在不同的对象上调用它。这就是函数原型方法 callapplybind 的用武之地。

区别

这三个方法都有相同的功能,即改变函数的 this 指向,但它们在调用方式和参数上有细微的区别:

  • call 方法接受两个参数:第一个参数是函数要执行的 this 值,第二个参数及以后的参数是传递给函数的参数。
  • apply 方法也接受两个参数:第一个参数是函数要执行的 this 值,第二个参数是一个数组,包含要传递给函数的参数。
  • bind 方法只接受一个参数:函数要执行的 this 值。它返回一个新的函数,该函数的 this 值被绑定到传入的参数。

源码实现

这三个方法的源码实现都比较简单,下面是它们的代码片段:

Function.prototype.call = function(context, ...args) {
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.call is not callable on non-functions");
  }

  context = context || globalThis;
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn;
  return result;
};

Function.prototype.apply = function(context, args) {
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.apply is not callable on non-functions");
  }

  context = context || globalThis;
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn;
  return result;
};

Function.prototype.bind = function(context, ...args) {
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.bind is not callable on non-functions");
  }

  const bound = (...args2) => {
    return this.call(context, ...args, ...args2);
  };

  bound.prototype = Object.create(this.prototype);

  return bound;
};

用法示例

以下是一些用法示例:

// 使用 call 方法
const obj = {
  name: "John Doe",
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const anotherObj = {
  name: "Jane Doe"
};

obj.greet.call(anotherObj); // Hello, my name is Jane Doe

// 使用 apply 方法
const numbers = [1, 2, 3, 4, 5];
const max = Math.max.apply(Math, numbers); // 5

// 使用 bind 方法
const button = document.getElementById("my-button");
button.addEventListener("click", function() {
  console.log(this); // <button id="my-button">...</button>
}.bind(button));

总结

函数原型方法 callapplybind 是改变函数执行时的 this 指向的重要手段,它们使一个函数能够被一个不同的对象调用。虽然这三个方法都有相同的功能,但在调用方式和参数上有细微的区别。掌握这些方法的用法可以帮助您更好地编写和使用 JavaScript 代码。