为啥调用“应用”而不是直接调用函数?

Posted

技术标签:

【中文标题】为啥调用“应用”而不是直接调用函数?【英文标题】:Why invoke "apply" instead of calling function directly?为什么调用“应用”而不是直接调用函数? 【发布时间】:2011-08-21 15:38:08 【问题描述】:

在查看 raphael 或 g.raphael 或其他库的源代码时,我注意到开发人员会这样做:

var val = Math.max.apply(Math, data_array);

为什么不直接调用函数呢,比如:

var val = Math.max(data_array);

谢谢。

【问题讨论】:

好的,所以我看到 Math.max 需要两个参数,所以我只是调用的示例 Math.max(array) 不起作用。所以,我想问题是我的问题中的第一行代码如何在 data_array 的每个元素上调用“max”? javascript.apply.call ftw!! 【参考方案1】:

通常您会使用apply()call() 来设置调用函数所属的上下文或对象。然后,该函数有效地成为该对象/上下文的方法,如果您需要访问上下文的字段,这将很有帮助。

这是 Javascript 原生调用函数的方式:

    从参数 1 到末尾创建一个参数列表 (argList) 第一个参数是thisValue 将此设置为 thisValue 并将 argList 作为其参数列表来调用函数

参考:Understanding JavaScript Function Invocation and "this"Yehuda Katz

因此,当直接调用函数时,实际上是在隐式地将默认上下文传递给函数。当您未使用 call()apply() 指定时,Javascript 将 'window''undefined' 作为上下文传递

answer 还提供了一个示例,可能有助于理解用法

【讨论】:

【参考方案2】:

.apply 通常用于意图调用带有参数值列表的可变参数函数,例如

Math.max([value1[,value2, ...]]) 函数返回零个或多个数字中的最大值。

Math.max(10, 20); // 20
Math.max(-10, -20); // -10
Math.max(-10, 20); // 20

Math.max() 方法不允许您传入数组。如果您有一个需要获取最大值的列表,您通常会使用Function.prototype.apply() 调用此函数,例如

Math.max.apply(null, [10, 20]); // 20
Math.max.apply(null, [-10, -20]); // -10
Math.max.apply(null, [-10, 20]); // 20

但是,从 ECMAScript 6 开始,您可以使用 spread operator:

扩展运算符允许在需要多个参数(用于函数调用)或多个元素(用于数组字面量)的地方扩展表达式。

Math.max(...[10, 20]); // 20
Math.max(...[-10, -20]); // -10
Math.max(...[-10, 20]); // 20

当使用可变参数调用函数时,你甚至可以添加额外的值,例如

Math.max(...[10, 20], 50); // 50
Math.max(...[-10, -20], 50); // 50

【讨论】:

【参考方案3】:

为什么调用“apply”而不是直接调用函数?让我们来个sn-p:

var obj = a: "apply";
func.call(obj, "parameter");
function func(para) 
    if(this.a === "apply")
        console.log('apply'); 
     
 

这里我们可以看到我们在一个函数中使用'this'(上下文),如果我们直接调用一个函数而不是我们没有任何上下文,那么它将使用window上下文,这是错误的。因此,如果您在函数中使用上下文而不是使用 apply 方法。

【讨论】:

【参考方案4】:

我认为Mozilla Docs的解释描述得很好:

您可以分配不同的 this 对象 调用现有函数时。 this 指的是当前对象, 调用对象。通过应用,您可以 写一次方法然后继承 它在另一个对象中,没有 重写新的方法 对象。

apply 与 call 非常相似,除了 对于它支持的参数类型。 您可以改用参数数组 一组命名的参数。和 应用,您可以使用数组文字, 例如,fun.apply(this, [name, value]) 或 Array 对象,用于 例如,fun.apply(这个,新的 数组(名称,值))。

关于参数:

thisArg 确定 fun 中 this 的值。如果 thisArg 为 null 或 未定义,this 将是全局的 目的。否则,这将是相等的 到 Object(thisArg) (这是 thisArg 如果 thisArg 已经是一个对象,或者一个 如果 thisArg,则为字符串、布尔值或数字 是的原始值 对应类型)。因此它是 总是如此 typeof this == 函数执行时的“对象”。

argsArray 对象的参数数组,指定参数 fun 应该被调用,或者 null 或者 如果没有参数应该是未定义的 提供给函数。

文档提供了一个很好的应用用例示例。在下面的示例中,apply 用于链接构造函数:

function product(name, value)

  this.name = name;
  if (value >= 1000)
    this.value = 999;
  else
    this.value = value;


function prod_dept(name, value, dept)

  this.dept = dept;
  product.apply(this, arguments);

prod_dept.prototype = new product();

// since 5 is less than 1000 value is set
var cheese = new prod_dept("feta", 5, "food");

// since 5000 is above 1000, value will be 999
var car = new prod_dept("honda", 5000, "auto");

请注意,在prod_dept 构造函数中,提供的this 引用prod_dept 对象,arguments 是传递给product 构造函数的参数数组。

【讨论】:

prod_dept.prototype = new product();此行完全不需要,请排除复杂性!【参考方案5】:

Math.max 默认不接受列表。 "apply" 允许您将列表解压缩为参数,以便 max 正常工作。

【讨论】:

以上是关于为啥调用“应用”而不是直接调用函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在我的代码中调用复制构造函数而不是移动构造函数?

为啥我可以使用 SELECT 而不是 CALL 调用 HSQL 函数?

为啥 std::apply 可以调用 lambda 而不是等效的模板函数?

在渲染期间,为啥要在 React.createElement 中包装一个函数式组件,而不是通过函数调用来使用它返回的元素呢?

当我递归调用它时,为啥我的函数返回以前的值而不是新的值?

为啥析构函数被调用两次而构造函数只被调用一次?