call、bind 和 apply 是 JavaScript 中 函数调用 的三个重要方法,它们都用于改变函数的 执行上下文(this) ,即改变函数内部 this 指向的对象。
call方法
定义:
call 方法允许你指定 this 上下文并立即调用该函数。call 方法的语法如下:
func.call(thisArg, arg1, arg2, ...);
-
thisArg:表示函数执行时this的值。 -
arg1, arg2, ...:是传递给函数的参数,这些参数会被逐一传递给原函数。 特点: -
立即执行:调用
call时,函数会立即执行。 -
可以传参:你可以通过
call传递多个参数给目标函数。
例子:
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Alice' };
// 使用 call
greet.call(person, 'Hello', '!'); // 输出:Hello, Alice!
解析:
call的第一个参数是thisArg,它会绑定到greet函数的this上。- 接下来的参数是传递给
greet函数的参数。
apply 方法
定义:
apply 方法与 call 方法非常相似,区别在于 参数传递的方式不同。apply 接收一个参数数组作为参数,而 call 是逐个传递参数。
func.apply(thisArg, [arg1, arg2, ...]);
-
thisArg:表示函数执行时this的值。 -
[arg1, arg2, ...]:一个包含参数的数组。
例子:
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Alice' };
// 使用 apply
greet.apply(person, ['Hello', '!']); // 输出:Hello, Alice!
bind 方法
定义:
bind 方法与 call 和 apply 的不同之处在于:bind 不会立即执行函数,而是返回一个新的函数,该函数在调用时会使用指定的 this 值和传入的参数。
const newFunc = func.bind(thisArg, arg1, arg2, ...);
-
thisArg:表示函数执行时this的值。 -
arg1, arg2, ...:是传递给新函数的参数。
特点:
- 不会立即执行:与
call和apply不同,bind不会立即执行函数,而是返回一个新的函数,直到该函数被调用时才执行。 - 可以提前绑定参数:你可以为返回的函数提供一些预设的参数,返回的函数会将这些预设的参数与后续调用时传入的参数一起使用
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Alice' };
// 使用 bind
const greetPerson = greet.bind(person, 'Hello');
greetPerson('!'); // 输出:Hello, Alice!
解析:
bind返回一个新的函数greetPerson,该函数已经绑定了this和greeting参数。- 当我们调用
greetPerson时,传入!作为第二个参数,最后输出Hello, Alice!。
call、apply 和 bind 之间的主要区别
| 特性 | call | apply | bind |
|---|---|---|---|
| 执行时机 | 立即执行 | 立即执行 | 返回一个新的函数,延迟执行 |
| 参数传递方式 | 参数逐个传递 | 参数通过数组传递 | 参数通过链式传递(可以部分预设) |
| 返回值 | 函数的返回值 | 函数的返回值 | 返回一个新的函数 |
练习
- 使用
call改变this
问题:
有一个 person 对象,包含 name 和 greet 方法。通过 call 方法,创建一个新的 employee 对象,并调用 greet 方法,使其显示正确的 this 值。
const person = {
name: 'Alice',
greet: function() {
console.log('Hello, ' + this.name);
}
};
const employee = {
name: 'Bob'
};
// 使用 call 改变 greet 函数中的 this
要求:
- 通过
call将person.greet的this指向employee。
- 使用
apply改变this
问题:
有一个 car 对象,它有一个 drive 方法,可以打印出 this 对象的属性 model。通过 apply 方法,借用 drive 方法并输出 car2 对象的 model。
const car = {
model: 'Tesla',
drive: function() {
console.log(this.model + ' is driving');
}
};
const car2 = {
model: 'BMW'
};
// 使用 apply 改变 this 的指向
要求:
- 通过
apply改变drive函数的this,并确保car2的model被正确输出。
- 使用
bind创建新的函数
问题:
有一个 counter 对象,它有一个 increment 方法,用于递增 count。请通过 bind 创建一个新的 incrementByTwo 方法,并将其应用于 counter 对象。
const counter = {
count: 0,
increment: function() {
this.count++;
console.log(this.count);
}
};
// 使用 bind 创建新的函数 incrementByTwo
要求:
- 通过
bind创建一个新函数,使其递增 2 次,而不是 1 次。
答案:
const counter = {
count: 0,
increment: function() {
this.count++;
console.log(this.count);
}
};
// 使用 bind 和闭包来创建每次递增 2 的方法
const incrementByTwo = function() {
this.increment();
this.increment();
}.bind(counter);
incrementByTwo(); // 输出: 1, 2
incrementByTwo(); // 输出: 3, 4
incrementByTwo(); // 输出: 5, 6
bind和call的结合使用
问题:
有一个 person 对象,包含 name 和 greet 方法。通过 bind 和 call 组合使用,先使用 bind 部分预设参数,再通过 call 调用函数。
const person = {
name: 'Charlie',
greet: function(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
};
// 使用 bind 和 call 组合
要求:
- 使用
bind先将this绑定到person,并预设一个greeting参数。 - 使用
call传递剩余的punctuation参数。
答案:
const greetPerson = person.greet.bind(person, 'Hello');
greetPerson.call(person,'!'); // 输出:Hello, Charlie!