前言
call
、apply
和 bind
是 JavaScript 中用于改变函数执行上下文(即 this 的指向)的方法。
call
在 JavaScript 中,call()
方法用于调用一个函数,并可以指定函数内部的 this
对象以及传递参数。
语法
function.call(thisArg, arg1, arg2,...)
特点和用途
- 更改函数的执行上下文:通过将一个对象作为第一个参数传递给
call()
方法,可以改变函数内部this
的指向。原本函数中的this
会被替换为call()
方法中指定的对象。
const person1 = { name: "张三" };
const person2 = { name: "李四" };
function greet() {
console.log(`Hello, ${this.name}!`);
}
// 使用 call 更改执行上下文
greet.call(person1); // 输出:"Hello, 张三!", greet()方法中的this指向 person1
greet.call(person2); // 输出:"Hello, 李四!", greet()方法中的this指向 person2
- 传递参数:除了第一个用于指定
this
对象的参数外,后续的参数会作为被调用函数的参数进行传递。
function sum(a, b) {
console.log(a + b)
return a + b;
}
// 使用 call 传递额外参数
const result = sum.call(null, 1, 2); // 3, a = 1, b = 2
- 模拟继承:可以使用
call()
方法来模拟继承,创建一个新对象并继承另一个对象的属性和方法。
const parent = function (name) {
this.name = name;
};
parent.prototype.greet = function () {
console.log(`Hello, ${this.name}!`);
};
const child = function (name) {
parent.call(this, name); // 调用父类构造函数
};
// 继承父类方法
child.prototype = Object.create(parent.prototype);
const childInstance = new child("张三");
childInstance.greet(); // 输出:"Hello, 张三!"
注意事项
call()
方法只会在函数执行时更改this
的指向,不会永久绑定函数到指定的对象。- 对于箭头函数,
call()
方法不能改变其this
的指向,因为箭头函数的this
是在定义时就确定的,并且基于其所在的词法环境。 - 如果不传入第一个参数或传入
null
、undefined
,在非严格模式下,函数中的this
会指向全局对象(在浏览器中是 window 对象);在严格模式下,this
的值则为undefined
。
apply
在 JavaScript 中,apply()
方法通常用于函数调用,并可以指定一个额外的参数作为函数体内的“this
”对象。
语法
func.apply(thisArg, argsArray)
func
:要调用的函数。thisArg
:指定函数运行时的this
引用。如果该参数不是一个对象,那么它会被转换为对象。argsArray
:一个数组或类数组对象,包含要传递给函数的参数列表。
示例:
function sum(a, b) {
return a + b;
}
var obj = { };
var result = sum.apply(obj, [2, 3]);
// 这里将 obj 绑定为 sum 函数内部的 this 对象,[2, 3] 作为参数数组传递给 sum 函数
console.log(result);
用途
- 改变函数的
this
上下文:可以将函数的this
指向指定的对象。
function sayName() {
console.log(this.name);
}
const person = { name: "张三" };
sayName.apply(person); // 输出: 张三
在上述示例中,通过 apply()
方法将 sayName()
函数的 this
指向了 person
对象。
- 传递参数列表:通过数组的形式来传递函数的参数。
function sum(a, b, c) {
return a + b + c;
}
let nums = [11, 22, 33];
let result = sum.apply(null, nums);
console.log(result); // 输出: 66
这里使用 apply()
方法将数组 nums
中的元素作为参数传递给 sum()
函数。
注意事项
- 如果
apply()
方法的第二个参数不是一个有效的数组或者不是arguments
对象,将会报错TypeError
。 - 对于箭头函数,
call()
方法不能改变其this
的指向,因为箭头函数的this
是在定义时就确定的,并且基于其所在的词法环境。 - 如果不传入第一个参数或传入
null
、undefined
,在非严格模式下,函数中的this
会指向全局对象(在浏览器中是 window 对象);在严格模式下,this
的值则为undefined
。
比较 apply和call
相同点:
apply()
方法与 call()
方法非常类似,它们都可以改变函数的 this
对象并执行函数。
区别:
apply()
接受的是一个参数数组,call()
方法接受的是一个参数列表。
示例:
function multiply(a, b) {
return a * b;
}
let resultCall = multiply.call(null, 4, 6);
let resultApply = multiply.apply(null, [4, 6]);
console.log(resultCall);
console.log(resultApply);
bind
在 JavaScript 中,bind()
方法用于创建一个新的函数,该函数在被调用时,其this
值会被绑定到指定的对象,同时可以预设一些参数供调用时使用。
语法
let boundFunc = function.bind(thisArg, arg1, arg2,...)
thisArg
:调用绑定函数时作为this
参数传递给目标函数的值。如果使用new
运算符构造绑定函数,则忽略该值。
当在setTimeout
中使用bind
创建函数(作为回调提供)时,任何原始的thisArg
值都会被转换为对象。如果bind
函数的参数列表为空,或者thisArg
是null
或undefined
,执行作用域的this
将被视为新函数的thisArg
。arg1, arg2,...
:当目标函数被调用时,这些参数会被预置入绑定函数的参数列表中。boundFunc
: 返回值,返回一个原函数的拷贝,并拥有指定的this
值和初始参数。
用途
- 设置函数的执行上下文(this 指向):创建一个新函数,在调用这个新函数时,其
this
值会被指定为bind()
方法的第一个参数。这使得函数可以在不同的上下文中执行,而不必担心this
的指向问题。
示例:
const person = {
name: 'levi',
greet: function() {
console.log('hello,'+ this.name);
}
};
// 直接调用 greet 方法,此时 this 指向 person 对象
person.greet();
// 保存 greet 方法的引用,但此时 this 不指向 person 对象
let greetFunction = person.greet;
greetFunction();
// 使用 bind 方法将 greetFunction 的 this 绑定到 person 对象
let boundGreetFunction = greetFunction.bind(person);
boundGreetFunction();
const person = {
firstname: 'John',
lastname: 'Bob',
getFullName: function() {
console.log(this.firstname + "." + this.lastname);
}
};
// 直接调用 getFullName 方法,此时 this 指向 person 对象
person.getFullName(); // John.Bob
// 保存 getFullName 方法的引用,但此时 this 不指向 person 对象
let getFullNameFunc = person.getFullName ;
// getFullNameFunc(); // Error,TypeError: Cannot read properties of undefined (reading 'firstname')
// 使用 bind() 方法将 getFullNameFunc 函数的 this 绑定到 person 对象上
let boundGetFullNameFunc = getFullNameFunc.bind(person);
boundGetFullNameFunc(); // John.Bob
这里的 printFullName.bind(person)
作为 printFullName
的“绑定的(bound)变体”,绑定了 this=person
。
- 预设参数:除了绑定this之外,还可以在bind()方法中预设一些参数。这些参数会在新函数调用时被使用,而后续传递给新函数的参数将依次排在预设参数之后。
示例:
function greet(greeting, punctuation) {
console.log(greeting + ','+ this.name + punctuation);
}
const person = { name: 'levi' };
// 创建一个预设了参数的新函数 greetAlice
let greetAlice = greet.bind(person, 'hello');
greetAlice('!'); // hello,levi!
- 创建偏函数:可以固定部分参数,从而创建一个具有特定行为的新函数。
示例:
function sum(a, b) {
console.log(a + b);
}
// 创建一个预设了参数的新函数 boundSum
// sum 绑定this 为null, 绑定一个固定参数 1 ,返回一个新函数 boundSum
let boundSum = sum.bind(null, 1)
console.log( boundSum (2) ); // 3
- 在特定环境中使用特定的 this 值:例如在事件处理程序中,确保函数中的
this
指向正确的对象。在一些框架(如 React)中,处理事件时经常会用到。 - 作为构造函数使用:当使用
new
操作符调用通过bind()
绑定后的函数来创建实例时,原本通过bind()
设置的this
值会被忽略,但预设的参数仍然会生效。
apply、call和bind的区别
- 执行方式:
call
和apply
会在调用时立即执行函数。bind
不会立即执行函数,而是返回一个绑定了新this
的函数,稍后可以通过调用这个返回的函数来执行。
- 传参方式:
call
的第一个参数是要绑定的this
对象,后面可以逐个传入函数的参数。apply
的第一个参数是要绑定的this
对象,第二个参数是一个数组,数组中的元素将作为函数的参数。bind
的传参方式与call
类似,逐个传入参数,也可以在返回的函数调用时传入剩余参数。
- 修改
this
的性质:
call
和apply
只是临时修改一次this
的指向,即在调用它们的那次函数执行时改变this
。当再次调用原函数时,this
的指向还是原来的指向。bind
是永久修改函数的this
指向,它返回的新函数的this
永远被改变了,且绑定后无法再修改。