JS 面试题:.call 和.apply 有什么区别?
作者:admin 时间:2022-7-27 10:4:53 浏览:.apply
和 .call
都是 Function
对象的方法:Function.apply()
、Function.call()
。
JavaScript 中的每个函数都有许多附加方法,包括toString()
、call()
和apply()
。请记住, JavaScript 中的每个函数都是一个对象。现在我们将关注函数的两个方法:apply()
和call()
。
call() 方法
让我们从以下代码开始:
var x = 10;
function f()
{
console.log(this.x);
}
f();
输出
10
这里我们有一个名为f()
的全局函数。f()
使用this
关键字来引用x
,但请注意我们不会通过对象的实例调用该函数。那么这个引用的是什么对象呢?这将引用全局对象。全局对象是我们定义变量 x
的地方。上面的代码确实有效,并将在对话框中显示值 10
。
call()
和apply()
都是我们可以用来在方法调用期间分配this
指针的方法。例如,下面是我们如何使用call()
方法:
var x = 10;
var o = { x: 15 };
function f()
{
console.log(this.x);
}
f();
f.call(o);
输出
10
15
f()
的第一次调用将显示值 10
,因为它引用了全局对象。然而,第二次调用(通过call
方法)将显示值 15
。15
是对象o
中x
属性的值。call()
方法调用函数并将其第一个参数用作函数体内的this
指针。换句话说 -我们已经告诉运行时在函数f()
内部执行时引用什么对象作为this
。
我们还可以通过call()
将参数传递给目标函数:
var x = 10;
var o = { x: 15 };
function f(message)
{
console.log(message);
console.log(this.x);
}
f("invoking f");
f.call(o, "invoking f via call");
执行结果
apply() 方法
apply()
方法与call()
相同,只是 apply()
需要一个数组作为第二个参数。该数组表示目标方法的参数。
var x = 10;
var o = { x: 15 };
function f(message)
{
console.log(message);
console.log(this.x);
}
f("invoking f");
f.apply(o, ["invoking f through apply"]);
执行结果
apply()
方法很有用,因为我们可以构建一个函数,它不关心目标方法的签名。该函数可以使用apply()
通过数组将所有附加参数传递给目标方法。
var o = { x: 15 };
function f1(message1)
{
console.log(message1 + this.x);
}
function f2(message1, message2)
{
console.log(message1 + (this.x * this.x) + message2);
}
function g(object, func, args)
{
func.apply(object, args);
}
g(o, f1, ["the value of x = "]);
g(o, f2, ["the value of x squared = ", ". Wow!"]);
执行结果
参数标识符
这里的问题是笨拙的语法。我们强迫调用者将参数填充到一个数组中,所以我们调用apply()
。幸运的是,有一种方法可以使语法更简单,但我们必须再介绍一个主题:参数标识符。
在 JavaScript 中,每个函数本质上都有一个可变长度的参数列表。这意味着即使函数只使用一个参数,我们也可以将 5 个参数传递给函数。以下运行没有错误并显示“H”:
function f(message)
{
console.log(message);
}
f("H", "e", "l", "l", "o");
输出
H
如果我们确实想从f()
中访问其他参数,我们可以使用 arguments 关键字。arguments引用了一个 Arguments 对象,该对象具有长度属性,感觉就像一个数组。
function f(message)
{
// message param is the same as arguments[0]
for(var i = 1; i < arguments.length; i++)
{
message += arguments[i];
}
console.log(message);
}
// this will say "Hello"
f("H", "e", "l", "l", "o");
输出
Hello
正如你所知,arguments 在技术上不是一个数组,即使它像一个数组一样。arguments 具有长度属性,但没有split
、push
或pop
方法。我们可以对之前的g()
函数中的参数做的事情是将参数[1] 之后的传入参数复制到我们传递给apply
的数组对象中。
var o = { x: 15 };
function f(message1, message2)
{
console.log(message1 + (this.x * this.x) + message2);
}
function g(object, func)
{
// arguments[0] == object
// arguments[1] == func
var args = []; // empty array
// copy all other arguments we want to "pass through"
for(var i = 2; i < arguments.length; i++)
{
args.push(arguments[i]);
}
func.apply(object, args);
}
g(o, f, "The value of x squared = ", ". Wow!");
输出
The value of x squared = 225. Wow!
当我们调用g()
时,我们可以将其他参数作为参数传递,而不是将参数填充到数组中。
至此,我们已经掌握了理解call
和apply
所需的理论知识。
那么.apply 和.call 有什么区别呢?
解释它们差异的一个很好的助记符是:
.call
计算由逗号分隔的参数数量。
.call
方法接受一个或多个参数作为对象,并且需要明确列出,这意味着它是固定数量的参数。
foo.call(object, “other argument”, “another one”);
而
.apply
使用数组作为参数。
.apply
方法需要使用一个数组作为其第二个参数。如果你不知道要传递的参数数量或参数已经在数组中,则使用此方法。
foo.apply(object, [“argument1”, “argument2”, “argument3”]);
从下面程序的注释了解 .apply 和 .call 的区别
// .apply 和 .call 如何工作的?
/*
在这里,我们创建了 2 个变量,
x 变量只保存一个字符串值,
y 变量包含一个带有 x 属性的对象,我们稍后将使用 .call 或 .apply 方法访问该对象
*/
var x = 'hello';
var y = {
x: 'world' };
// 我们创建一个标准函数
function runThis() {
console.log(this.x);
};
/*
此调用将显示 hello 的值
因为我们在没有新实例的情况下调用了 runThis 函数
runThis 函数中的“this”关键字将引用全局对象 x 的值
*/
runThis(); // 显示 hello
/*
此调用将显示 world 的值
因为我们使用 .call 方法调用了 runThis,
.call 方法将其第一个参数值(y 对象)用于 runThis 函数内部的 this 关键字
所以在这里,我们将 y 对象引用到 'this' 关键字,并且
我们能够访问并显示 x 属性的值
*/
runThis.call(y); // 显示 world
// ======================================================================
// 所以 .call 和 .apply 的区别在哪里?
/*
在这里,我们创建了一个名为“a”的变量,其中包含一个具有“b”属性的对象值
*/
var a = {
b: "foo" };
/*
我们声明了一个接受“msg”参数的函数,并且
显示与 msg 参数连接的对象的属性值
*/
function displayMsg(msg) {
console.log(this.b + " " + msg);
}
/*
我们使用 .call 和 .apply 方法调用了 displayMsg 函数
传入一个对象和一个参数
*/
displayMsg.call(a, "bar"); // .call 接受一个或多个对象作为它的参数
displayMsg.apply(a, ["buzz"]); // .apply 需要一个数组作为它的参数
相关文章
- 站长推荐