likes
comments
collection
share

JavaScript中的call,apply,bind方法详解及简单实现

作者站长头像
站长
· 阅读数 12

call, apply, 和 bindJavaScript 中常用的函数。它们的作用是在函数调用时动态地改变函数的上下文。具体来说,它们可以指定函数中的 this 指向哪个对象,以及传递参数给函数。

call

call 函数允许你在一个特定的上下文中调用一个函数。它的语法如下:

function.call(context, arg1, arg2, ...)

其中,context 是指定函数中的 this 关键字指向的对象,arg1, arg2, ... 是传递给函数的参数。

下面是一个例子,其中 obj 对象中有一个 greeting 方法:

const obj = {
  name: 'Alice',
  greeting: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

obj.greeting(); // 输出 "Hello, my name is Alice"

现在,假设我们要将 greeting 方法应用于另一个对象 person,并让 this 关键字指向 person 对象。我们可以使用 call 函数来实现:

const person = {
  name: 'Bob'
};

obj.greeting.call(person); // 输出 "Hello, my name is Bob"

这里,我们使用 call 函数将 obj 对象的 greeting 方法应用于 person 对象。由于我们使用了 call 函数,并将 person 对象作为参数传递给它,因此 greeting 方法中的 this 关键字会指向 person 对象,而不是 obj 对象。

apply

apply 函数与 call 函数类似,它也允许你在一个特定的上下文中调用一个函数。不同之处在于,apply 函数需要将参数作为数组传递。它的语法如下:

function.apply(context, [argsArray])

其中,context 是指定函数中的 this 关键字指向的对象,argsArray 是一个数组,其中包含要传递给函数的参数。

下面是一个例子,其中 obj 对象中有一个 greeting 方法:

const obj = {
  name: 'Alice',
  greeting: function(city, country) {
    console.log(`Hello, my name is ${this.name} and I am from ${city}, ${country}`);
  }
};

obj.greeting('New York', 'USA'); // 输出 "Hello, my name is Alice and I am from New York, USA"

现在,假设我们要将 greeting 方法应用于另一个对象 person,并让 this 关键字指向 person 对象,并且传递一个数组作为参数。我们可以使用 apply 函数来实现:

const person = {
  name: 'Bob'
};

obj.greeting.apply(person, ['London', 'UK']); // 输出 "Hello, my name is Bob and I am from London, UK"

这里,我们使用 apply 函数将 obj 对象的 greeting 方法应用于 person 对象,并将数组 ['London', 'UK'] 作为参数传递给它。由于我们使用了 apply 函数,并将 person 对象和数组作为参数传递给它,因此 greeting 方法中的 this 关键字会指向 person 对象,而参数 'London''UK' 也会被传递给 greeting 方法。

bind

bind 函数与 callapply 函数不同,它不会立即调用函数。相反,它返回一个新函数,该函数将绑定到指定的上下文,当该函数被调用时,它将以指定的上下文运行。它的语法如下:

function.bind(thisArg, arg1, arg2, ...)

其中,thisArg 是指定函数中的 this 关键字指向的对象,arg1, arg2, ... 是传递给函数的参数。与 call 和 apply 不同,bind 函数不会立即调用函数,而是返回一个新的函数,你可以将它存储在变量中,然后在需要时调用。

下面是一个例子,其中 obj 对象中有一个 greeting 方法:

const obj = {
  name: 'Alice',
  greeting: function(city, country) {
    console.log(`Hello, my name is ${this.name} and I am from ${city}, ${country}`);
  }
};

const person = {
  name: 'Bob'
};

const boundGreeting = obj.greeting.bind(person, 'London');

boundGreeting('UK'); // 输出 "Hello, my name is Bob and I am from London, UK"

这里,我们使用 bind 函数将 obj 对象的 greeting 方法绑定到 person 对象上,并且传递 'London' 参数。bind 函数返回一个新的函数 boundGreeting,我们将其存储在变量中。当我们调用 boundGreeting('UK') 时,它会以 person 对象为上下文,以 'London''UK' 作为参数调用 greeting 方法。

简单实现

以下是一个简单的实现,展示了如何使用 JavaScript 实现 call、apply 和 bind 函数:

// 实现 call 方法
Function.prototype.myCall = function (context, ...args) {
  // 如果 context 参数为空,则默认为 window 对象
  context = context || window;
  // 使用 Symbol 函数创建一个唯一的标识符
  const fnSymbol = Symbol();
  // 将原始函数存储为 context 对象的属性
  context[fnSymbol] = this;
  // 调用函数并将结果存储在 result 变量中
  const result = context[fnSymbol](...args);
  // 删除 context 对象的属性
  delete context[fnSymbol];
  // 返回函数的结果
  return result;
};

// 实现 apply 方法
Function.prototype.myApply = function (context, args) {
  // 如果 context 参数为空,则默认为 window 对象
  context = context || window;
  // 使用 Symbol 函数创建一个唯一的标识符
  const fnSymbol = Symbol();
  // 将原始函数存储为 context 对象的属性
  context[fnSymbol] = this;
  // 调用函数并将结果存储在 result 变量中
  const result = context[fnSymbol](...args);
  // 删除 context 对象的属性
  delete context[fnSymbol];
  // 返回函数的结果
  return result;
};

// 实现 bind 方法
Function.prototype.myBind = function (context, ...args) {
  // 将 this 存储在 fn 变量中
  const fn = this;
  // 返回一个新的函数,该函数将传入的参数与新函数的参数合并,并在新的上下文中使用 apply 调用原始函数
  return function (...newArgs) {
    return fn.apply(context, [...args, ...newArgs]);
  };
};

这里,我们使用原型方法给 Function 对象添加了 myCallmyApplymyBind 方法。 myCallmyApply 非常相似,它们的不同之处在于参数传递方式。 myCall 函数使用剩余参数语法 ...args 来传递参数,而 myApply 函数接受一个数组作为参数。 myBind 函数返回一个新的函数,该函数接受一个参数,并将其与 myBind 中传递的参数合并,然后调用原始函数。

对于 myCallmyApply,我们首先将传入的 context 参数与 window 对象进行比较,如果 context 是空的,则默认为全局 window 对象。然后,我们使用 Symbol 函数创建一个唯一的标识符 fnSymbol,并将原始函数存储为 context[fnSymbol] 的属性。我们然后调用函数,将结果存储在 result 变量中,并使用 delete 关键字从 context 对象中删除函数属性。最后,我们返回函数的结果。

对于 myBind,我们首先将 this 存储在 fn 变量中,然后返回一个新的函数,该函数使用剩余参数语法将 myBind 中传递的参数与新函数的参数合并,并在新的上下文中使用 apply 调用原始函数。

这只是一个简单的实现,真正的 callapplybind 函数会处理更多的情况,例如处理原始类型的 this 值,传递参数的类型,等等。