Javascript 为什么知道.call()和.apply()之间的区别如此重要?

Javascript 为什么知道.call()和.apply()之间的区别如此重要?,javascript,Javascript,我一直在阅读一些关于JavaScript面试问题的网页,我注意到很多网页都说了解.call()和.apply()之间的区别很重要 例如: 就我的一生而言,我无法理解为什么这被认为如此重要。有人知道吗?对我来说,这可能和知道js长日期和js短日期之间的区别一样重要(也就是说,这对我来说似乎并不重要)。我的直觉告诉我,某个有影响力的人在某个时候说,知道.call()和.apply()之间的区别是至关重要的,每个人都只是照搬了这个人的话。但也许我误解了关于.call()和.apply()的某些内容

我一直在阅读一些关于JavaScript面试问题的网页,我注意到很多网页都说了解.call()和.apply()之间的区别很重要

例如:


就我的一生而言,我无法理解为什么这被认为如此重要。有人知道吗?对我来说,这可能和知道js长日期和js短日期之间的区别一样重要(也就是说,这对我来说似乎并不重要)。我的直觉告诉我,某个有影响力的人在某个时候说,知道.call()和.apply()之间的区别是至关重要的,每个人都只是照搬了这个人的话。但也许我误解了关于.call()和.apply()的某些内容?我不确定。

简单地说,更高级的Javascript类型偶尔需要使用
.call()
.apply()
,尤其是尝试代理函数和传递参数或控制
this
值的代码

如果你在做这些事情,那么你必须知道
.call()
.apply()
是如何工作的,这样你才能使用正确的方法(因为它们的工作方式不同)。如果您没有做这些类型的高级工作,那么很多Javascript都可以不用它们来编写

作为一个面试问题,我认为这是一个合理的测试,测试你是否理解Javascript中参数传递、调用方法和
这个
处理的一些更高级的细微差别。如果你想在这些类型的问题上做得很好,你需要了解
.call()
.apply()
,现在如何解释它们的作用,以及如何和何时使用它们,以及何时使用它们

这个
的整个概念以及它是如何在Javascript中控制或设置的,经验较少的Javascript程序员往往不太了解(见鬼,我甚至看到一些经验丰富的开发人员不太了解它),因此对候选人进行与此相关的测试是合理的,
.call()
.apply()
在某种程度上是这方面的核心

如果您正在开发库或框架代码,那么您在工作中更可能使用
.call()
.apply()


以下是一个基本总结:

Javascript中的每个函数都是具有某些属性的对象。因此,除了能够调用像
func()
这样的函数外,还可以引用它的属性,比如
func.length

每个函数的两个属性是
.call()
.apply()
。它们都允许您以不同的方式调用函数,而不仅仅是将其作为
func()
调用

.call()

当然,我们都知道,如果您只想使用固定数量的参数调用函数,那么您可以只执行
func(arg1,arg2)
,这些参数将被传递给函数,但是如果您想控制传递这些固定参数时
这个
值将是什么呢?将func调用为
func(arg1,arg2)
将导致该函数中的
值设置为浏览器中的
窗口中的全局对象,或者在严格模式下运行时设置为
未定义
。Javascript中的每个函数调用,例如
func(arg1,arg2)
都会以这种方式重置
this
指针。这就是
.call()
的用武之地。您可以执行:

func.call(someThisValue, arg1, arg2)
它将执行与
func(arg1,arg2)
相同的操作,只是它将导致
this
指针设置为
someThisValue

注意:您也可以只使用一个参数来使用
.call()
,它将只设置
这个
指针,而不传递任何参数

func.call(someThisValue)
.apply()

但是,如果参数列表不是固定的,并且参数位于可变长度数组或类似数组的对象中,该怎么办。您不能真正使用
.call()
,因为您不能为每个可能的参数列表键入正确的
.call()
语句。这就是
.apply()
的用武之地。
.apply()
的一个规范用法是当您试图传递来自其他函数调用的参数时。当您代理其他函数调用以稍微修改它们的行为,但仍然调用原始函数时,这是很常见的,原始函数或者具有各种形式(因此您不确切知道传递的参数),或者许多不同类型的函数调用都通过此代理。在这种情况下,您可以使用
.apply()
,通常与
参数
对象一起使用。您可以在中看到这方面的示例

假设我想挂接一些名为
doIt()
的现有函数用于日志记录,并且
doIt()
有许多不同的方法可以调用它。使用
.apply()
,我可以这样做:

// regular function already defined in your program
function doIt(arg1, arg2) {
    // do something
}

// then actual usage elsewhere in the program would be just this:
doIt("foo", "bar");

// now install a proxy that can intercept all calls to doIt() and
// add some behavior before and after
(function(origDoIt) {
     // replace doIt function with my own proxy
     doIt = function() {
         console.log("before doIt()");
         // call the original function with all the arguments and this pointer
         // that were passed
         var retVal = origDoIt.apply(this, arguments);
         console.log("after doIt()");
         return retVal;
     }
})(doIt);

仅供参考,
.apply()
也可用于设置
指针,如下所示:

func.apply(someThisValue)
在该特定情况下(仅在该情况下),它的工作原理与
.call()
相同


下面是我最喜欢的
.apply()
用法之一。
Math.max()。因此:

Math.max(1,2,3,4)
将返回
4

但是,使用
.apply()
,我们可以在任何数组中找到最大数

var list = [999,888,777,666,555,444,333,1000];
var m = Math.max.apply(Math, list);
console.log(m);    // 1000
我们使用
.apply()
将整个
列表
数组作为参数发送到
Math.max()
,因此它将在整个数组上运行

注意,当ES6在任何地方或在特定的执行环境中完全实现时,您也可以使用新的spread操作符来执行类似的操作
var list = [999,888,777,666,555,444,333,1000];
var m = Math.max(...list);
console.log(m);    // 1000