JavaScript中使用Function.call.apply的目的是什么?

16

我在浏览JavaScript Garden时,偶然发现了用于创建“快速、未绑定包装器”的Function.call.apply hack。它说:

Another trick is to use both call and apply together to create fast, unbound wrappers.

function Foo() {}

Foo.prototype.method = function(a, b, c) {
    console.log(this, a, b, c);
};

// Create an unbound version of "method" 
// It takes the parameters: this, arg1, arg2...argN
Foo.method = function() {

    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
    Function.call.apply(Foo.prototype.method, arguments);
};

我不明白的是为什么要使用Function.call.apply,而Function.apply就足够了。毕竟,它们两个在语义上是等价的。


我已经发布了一个链接到JavaScript Garden。如果你想直接跳转到包含该帖子的部分,请点击这里 - Aadit M Shah
谢谢,- 看起来他们暗示这个黑科技可以提高一些速度.. 但是,真的,不要使用它 - c69
2
@Aadit Function.call.applyFunction.apply在这里不能相同,因为前者应用了Function.call,而后者尝试应用Function构造函数。详情请参见我的答案,但我敢打赌,如果Ivo Wetzel来回答这个问题,他的回答会更优雅和易懂。这是相当深奥的东西。我同意,除非你想让同事花费半个小时来理解它,否则你可能不应该使用它。 :) - Ray Toal
2
我认为 Function.call 实际上有点误导。它应该是 Function.prototype.call,因为 Function.call 可以被重写。毕竟,Function 只是一个函数对象。(是的,这很难理解) - user123444555621
1
@Pubbaa80 尽管在没有什么恶意的情况下Function.call === Function.prototype.call,我完全同意你的看法。但是请注意, JSON.stringify(Object.getOwnPropertyDescriptor(Function.prototype, "call")) 返回{"writable":true,"enumerable":false,"configurable":true}因此真正邪恶的人也可以覆盖Function.prototype.call。哎呀。 - Ray Toal
2个回答

14
不,Function.call.applyFunction.apply在这种情况下并不相同。 假设原始调用者调用:
Foo.method(t, x, y, z)

使用call和apply结合在一起,就像JavaScript Garden代码中的那样。这将执行:
Function.call.apply(Foo.prototype.method, arguments);

这个概念可以松散地理解为(在数组表示法中写成arguments):

Function.call.apply(Foo.prototype.method, [t, x, y, z]);

这将使用this==Foo.prototype.method调用Function.call

Foo.prototype.method.call(t, x, y, z)

这段代码调用了Foo.prototype.method,并将this设置为t,传递了参数xyz。非常棒,就像在注释中所说的那样,我们成功地创建了一个包装器。

现在假设你只使用Function.apply而不是Function.call.apply,因为你声称它们在语义上等价。你会得到以下代码:

Function.apply(Foo.prototype.method, arguments);

这个词(宽泛地说)是指

Function.apply(Foo.prototype.method, [t, x, y, z]);

这段代码调用函数Function(呃!)并将this设置为Foo.prototype.method,参数为txyz

完全不同。


但是这种包装的目的是什么? - c69
3
目的是在不将Foo.prototype.method绑定到Foo实例的情况下调用它。相反,您传递自己的值作为Foo.prototype.method内部this的值。 为什么要这样做?也许你想进行一些函数式编程,而你想要映射、减少或筛选的函数恰好是一个绑定方法。如果您不希望将其绑定到对象,则调用“未绑定包装器”。虽然这个术语很奇怪,因为最终你还是将该方法绑定到自己的对象上(但可以忽略它...)。 - Ray Toal
好的,据我理解,以下语句是等价的:Function.call.apply(Foo.prototype.method, arguments); Foo.prototype.method.apply(arguments[0], Array.prototype.slice.call(arguments, 1));它们都可以被替换为创建未绑定包装器。然而,第一种方法更短、更快,并且更直观。太棒了! - Aadit M Shah

4

这意味着您可以在另一个对象上使用来自某个对象的方法。

一个很好的例子是所有函数都具有的参数变量,它类似于数组但不是数组,因此您可以调用数组的方法,例如:

Array.prototype.join.call(arguments, ",");

1
但是在原问题中,“apply”位是什么意思? - Cameron Skinner
1
@CameronSkinner:函数对象的apply方法会执行该函数,并将第一个参数视为该执行中的this,将第二个参数视为argumentsfn.apply(a, [b, c])等同于fn.call(a, b, c) - eyelidlessness

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接