apply、bind、call对比

在 JavaScript 中,applybindcall 是用于改变函数执行上下文(即 this 关键字)的方法。它们在函数调用时非常有用。下面分别介绍这三个方法,并给出手写实现的代码。

1. apply

apply 方法调用一个函数,并显式地指定 this 的值和参数列表(以数组或类数组对象的形式)。

1
2
3
4
5
6
7
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.apply(person, ["Hello", "!"]); // 输出: Hello, Alice!

2. call

call 方法与 apply 类似,也是调用一个函数,并显式地指定 this 的值和参数列表(以逗号分隔的形式)。

1
2
3
4
5
6
7
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.call(person, "Hello", "!"); // 输出: Hello, Alice!

3. bind

bind 方法创建一个新的函数,当这个新函数被调用时,其 this 值会被指定为 bind 方法的第一个参数,其余参数将作为新函数的参数。

1
2
3
4
5
6
7
8
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

const greetPerson = greet.bind(person);
greetPerson("Hello", "!"); // 输出: Hello, Alice!

手写实现

下面是 applycallbind 的手写实现:

apply

1
2
3
4
5
6
7
8
9
10
11
12
13
Function.prototype.myApply = function (context = window, args = []) {
if (typeof this !== "function") {
throw new TypeError(this + " is not a function");
}

const fnSymbol = Symbol();
context[fnSymbol] = this;

const result = context[fnSymbol](...args);

delete context[fnSymbol];
return result;
};

call

1
2
3
4
5
6
7
8
9
10
11
12
13
Function.prototype.myCall = function (context = window, ...args) {
if (typeof this !== "function") {
throw new TypeError(this + " is not a function");
}

const fnSymbol = Symbol();
context[fnSymbol] = this;

const result = context[fnSymbol](...args);

delete context[fnSymbol];
return result;
};

bind

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== "function") {
throw new TypeError(this + " is not a function");
}

const self = this;

return function (...newArgs) {
return self.apply(context, args.concat(newArgs));
};
};

示例

1
2
3
4
5
6
7
8
9
10
11
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.myApply(person, ["Hello", "!"]); // 输出: Hello, Alice!
greet.myCall(person, "Hello", "!"); // 输出: Hello, Alice!

const greetPerson = greet.myBind(person);
greetPerson("Hello", "!"); // 输出: Hello, Alice!

对比

方法 是否调用 参数形式
apply 数组或类数组对象
call 逗号分隔的参数列表
bind 逗号分隔的参数列表

通过这些手写实现,可以更好地理解 applycallbind 的内部工作原理。