JavaScript的call、apply、bind

113 阅读4分钟

JavaScript 中的 callbind 和 apply 都是函数的方法,它们用于控制函数的执行上下文和参数。

一、函数使用方法介绍

call:

  • call 方法可以调用一个函数,并允许你直接设置函数的 this 值。
  • 语法:func.call(thisArg, arg1, arg2, ...)
  • thisArg 是可选的,并用作函数内部的 this 值。
  • 其余参数是传递给函数的参数列表。
  • 示例:
function Person(name) {  
  this.name = name;  
  this.greet = function() {  
    console.log('Hello, ' + this.name);  
  };  
}  
var alice = new Person('Alice');  
var greetAlice = alice.greet;  
greetAlice.call(alice); // 输出:Hello, Alice;

apply:

  • apply 方法类似于 call,但它允许你传递一个数组作为参数。
  • 语法:func.apply(thisArg, [argsArray])
  • thisArg 是可选的,并用作函数内部的 this 值。
  • argsArray 是一个数组,其中的元素将作为单独的参数传递给函数。
  • 示例:
function greet(greeting, punctuation) {  
  console.log(greeting + ', ' + this.name + punctuation);  
}  
var alice = { name: 'Alice' };  
var greetAlice = greet;  
greetAlice.apply(alice, ['Hello', '!']); // 输出:Hello, Alice!;

bind:

  • bind 方法创建一个新的函数,该函数在调用时将指定的 this 值和任何参数传递给提供的函数。
  • 语法:func.bind(thisArg[, arg1[, arg2[, ...]]])
  • thisArg 是可选的,并用作函数内部的 this 值。
  • 后续参数将绑定为提供的函数的参数。如果没有提供后续参数,则绑定为 undefined
  • 示例:
function greet(greeting, punctuation) {  
  console.log(greeting + ', ' + this.name + punctuation);  
}  
var alice = { name: 'Alice' };  
var greetAlice = greet.bind(alice, 'Hello');  
greetAlice('!'); // 输出:Hello, Alice!;

二、函数使用情景

1: 改变函数上下文 (this 值)

function Person(name) {  
    this.name = name;  
    this.greet = function() {  
        console.log('Hello, ' + this.name);  
    };  
}  
  
var alice = new Person('Alice');  
  
// 使用 call 改变 greet 方法的 this 值  
alice.greet.call(alice); // 输出: Hello, Alice

2: 动态地改变函数参数

function greet(greeting, punctuation) {  
    console.log(greeting + ', ' + this.name + punctuation);  
}  
  
var person = { name: 'Alice' };  
  
// 使用 apply 传递参数列表  
greet.apply(person, ['Hello', '!']); // 输出: Hello, Alice!

3: 创建可复用的代码 (回调函数)

function logTime() {  
    console.log('Current time is: ' + new Date().toLocaleTimeString());  
}  
  
// 使用 bind 创建一个新的函数,该函数始终以特定时间格式输出时间  
var logTimeFormatted = logTime.bind(null);  
logTimeFormatted(); // 输出: Current time is: HH:mm:ss (例如: 14:30:20)

4: 实现函数柯里化

函数柯里化(Currying)是一种将多参数的函数转换成一系列单参数函数的技巧。

在JavaScript中,可以使用bind()方法来实现函数柯里化。通过将函数绑定到特定的上下文和参数,可以创建一个新的函数,该函数将始终使用绑定的上下文和参数来调用原始函数。然后,可以使用这个新的函数来逐步传递参数,从而实现函数柯里化的效果。

下面是一个使用bind()方法实现函数柯里化的示例:

function greet(name, punctuation) {  
  console.log(name + punctuation);  
}  
  
// 使用 bind 方法创建新的函数,将 name 参数绑定为 'Alice'  
var greetAlice = greet.bind(null, 'Alice');  
  
// 调用新函数,逐步传递参数  
greetAlice('!'); // 输出:Alice!

三、实现call、apply、bind

1.实现call函数

Function.prototype.myCall = function (context) {
  //判断调用对象
  if (typeof this !== "function") {
    console.error("type error");
  }
  //获得参数
  let args = [...arguments].slice(1),
    result = null;
  //判断context是否传入,如果未传入则设置为window
  context = context || window;
  //将调用函数设为对象的方法
  context.fn = this;
  //调用函数
  result = context.fn(...args);
  //将属性删除
  delete context.fn;

  return result;
};

function myFunction(a, b) {
  console.log(this.name + " " + a + " " + b);
  return "运行";
}

var obj = { name: "John" };

myFunction.myCall(obj, 1, 2); // 输出 "John 1 2"

2.实现apply函数

Function.prototype.myApply = function (context) {
  //判断调用对象是否为函数
  if (typeof this !== "function") {
    throw new TypeError("error");
  }
  let result = null;
  //判断context是否存在,如果未传入则为window
  context = context || window;
  //将函数设为对象的方法
  context.fn = this;
  //调用方法
  if (arguments[1]) {
    result = context.fn(...arguments[1]);
  } else {
    result = context.fn();
  }
  //将属性删除
  delete context.fn;
  return result;
};

var obj = { name: "John" };

myFunction.myApply(obj, [3, 4]); // 输出 "John 3 4"

3.实现bind函数

Function.prototype.myBind = function (context) {
  //判断调用对象是否为函数
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }
  //获取参数
  var args = [...arguments].slice(1),
    fn = this;
  return function Fn() {
    console.log("this instanceof Fn==", this instanceof Fn);
    //根据调用方式,传入不同绑定值
    //this instanceof Fn 的判断是为了确定当前上下文是否为 Fn 类型的实例。如果是,那么原函数的上下文将被保留;如果不是,那么上下文将被设置为传入的 context。
    // 这个判断非常重要,因为在 JavaScript 中,函数的上下文可以在运行时动态地改变。如果不进行这个判断,那么即使传入了新的上下文,由于 JavaScript 的动态性,this 也可能不会如预期地被绑定到传入的上下文上。
    return fn.apply(
      this instanceof Fn ? this : context,
      args.concat(...arguments)
    );
  };
};

function myFunction(age) {
  console.log(this.name, age);
}

var obj = {
  name: "Mike",
};

var myBoundFunction = myFunction.myBind(obj, 222);
myBoundFunction();