【问题标题】:Difference between Function.call, Function.prototype.call, Function.prototype.call.call and Function.prototype.call.call.callFunction.call、Function.prototype.call、Function.prototype.call.call 和 Function.prototype.call.call.call 的区别
【发布时间】:2017-04-15 08:57:35
【问题描述】:

我在stackoverflow上查了很多类似的问题,比如call.call 1call.call 2,但我是新手,不能在任何地方发表评论。我希望我能找到关于 JavaScript 解释器如何执行这些函数的全面而透彻的解释,并在这里提出一个新问题。

这里有一些例子:

function my(p) { console.log(p) }
Function.prototype.call.call(my, this, "Hello"); // output 'Hello'

上面的例子是Function.prototype.call的标准用法,比较容易理解。我的理解是:“我的”作为函数对象执行其继承的方法“Function.prototype.call”,即。 my.(Function.prototype.call) => my.call(this, 'Hello')

Function.call.call(my, this, "Hello") // output 'Hello'

与上面的示例相比,我在这里感到困惑。我不知道 JavaScript 解释器是如何工作的。 'my' 将 Function.call 和 Function.prototype.call 视为相同的方法?

Function.prototype.call(my, this, 'Hello2') // output nothing
Function.call(my, 'Hello2') // output nothing

我无法解释为什么这个语句没有抛出错误?其实我不知道 Function.prototype.call 作为方法是怎么工作的。

Function.prototype.call.call.call(my, this, "Hello3"); // output 'Hello3'

我无法解释 JavaScript 解释器如何解释上述语句?从右到左解释“呼叫”?那么 my.(Function.prototype.call.call) 是什么意思呢?

Function.prototype.call.call.call.call(my, this, "Hello4"); // output 'Hello4'

为什么我可以在这里放任意数量的'.call',并且输出是一样的?不是每个调用都使用一个参数作为“this”对象,这应该意味着只有三个参数是不够的吗?与上一个类似的问题

然后是更多例子:

var $ = Function.prototype.call
$(my, this, 'Hello5') // Exception: TypeError: Function.prototype.call called on incompatible Proxy

为什么它不输出任何内容,就像上面的一个例子一样?

var v = Function.prototype.call.call 
v(my, this, 'Hello6') // Exception: TypeError: Function.prototype.call called on incompatible Proxy

这是否意味着,当使用变量 v 时,JavaScript 解释器会尝试单独解释 v,而没有看到后面有参数?然后解释器认为'this'是全局变量'window'?我不知道解释器在 v() 和 Function.prototype.call.call() 之间的工作方式有何不同

有人可以帮忙吗?谢谢!

【问题讨论】:

  • Function.prototype.call.call === Function.prototype.call // true Function.call === Function.prototype.call.call // true
  • 谢谢。是的,它们是严格相等的,但是你能解释一下为什么当它们后面跟着括号中的相同参数时它们的输出会不同吗?
  • 我的回答中没有做到吗?

标签: javascript functional-programming prototype call


【解决方案1】:

Function.prototype.call.call(my, this, "Hello"); 表示:

my 用作this 参数(函数上下文)用于called 的函数。在这种情况下,Function.prototype.call 被调用。

因此,Function.prototype.call 将使用 my 作为其上下文来调用。这基本上意味着 - 这将是要调用的函数。

它将使用以下参数调用:(this, "Hello"),其中this 是要在要调用的函数内设置的上下文(在本例中为my),并且要传递的唯一参数是"Hello" 字符串。

只要您的 my 函数不以任何方式使用 this 上下文 - 您可以传递任何内容:

Function.prototype.call.call(my, 123, 'Hello2') // outputs "Hello2"

只要Function.prototype.call.call 引用了与Function.prototype.call.call.call 相同的函数(以及Function.callmy.call)等等,您可以添加任意数量的.call 属性访问,并且它不会改变任何东西。

关于你的第二个问题:

var $ = Function.prototype.call
$(my, this, 'Hello5')

不起作用,因为函数调用的上下文是在调用期间动态设置的(除非它与 .bind() 绑定或者是 ES2015 箭头函数)。

所以当你以$ 调用它时,上下文没有显式设置(因为它是一个变量,而不是一个对象),所以默认情况下它设置为undefined 或全局对象(取决于你如何运行你的代码)。

【讨论】:

  • 当我调用一个函数变量时,为什么JavaScript解释器没有将$翻译成Function.prototype.call.call,然后看到$后面是一个括号内的对象?它可以通过这种方式找到“this”对象。所以还是不知道这里的函数是怎么执行的。
  • 它没有,因为这是标准告诉解释器工作的方式:上下文是在函数调用期间确定的。而$(...) 没有任何上下文。
  • 我应该参考ECMA-262吗?如果有,请问是哪个部分?我不太清楚上下文的概念。 “当您将其作为 $ 调用时,上下文未明确设置”,您的意思是, this 未明确设置?
  • @nlite 是正确的。在这种情况下未设置的是thisecma-international.org/ecma-262/6.0/…见第5条
【解决方案2】:

每个对象都链接到一个原型对象。当试图检索对象上不存在的属性时,将查找其原型对象。 Read more.

this 是 JavaScript 函数中可用的特殊变量。它的值取决于你如何调用函数,而不是你如何定义它(有一些例外)。 Read more.


如您所见,call 是在Function.prototype 上定义的,这意味着所有函数对象都可以访问它。这是一个特殊的函数,它允许您在调用函数时显式设置this 的值。

一种常见的用法是在类数组对象上调用数组函数,这些对象不继承自Array.prototype

function f(){
    const args = Array.prototype.slice.call( arguments );
    // `args` is now a real array with all the privileged functions.
}

回到你的例子,

function my(p) { console.log(p) }
Function.prototype.call.call(my, this, "Hello"); // output 'Hello'

你在Function.prototype.call上打电话给call

相当于

function my(p) { console.log(p) }
my.call(this, "Hello"); // output 'Hello'

由于call 是在Function.prototype 上定义的,并且它本身就是一个函数,所以当您调用call 两次时,您将检索到相同的函数。这就是为什么您可以无限期地链接它们。

function my(p) { console.log(p) }
const call1 = Function.prototype.call;
const call2 = Function.prototype.call.call;    // Same as `call1.call`.
const call3 = my.call;

console.log( call1 === call2 );
console.log( call1 === call3 );

现在到问题的第二部分。 this 如何在函数内部工作。 Read more.

要记住的是,它的值是动态确定的,取决于你如何调用函数,而不是你如何定义它(有一些例外)。

当您这样做时,call 函数的 this 值将是当前范围内的任何值(在全局范围内,严格模式下为 undefined,松散模式下为 window)。

var v = Function.prototype.call.call 
v(my, this, 'Hello6') // Exception: TypeError: Function.prototype.call called on incompatible Proxy

这就是您收到错误的原因,this 不是函数,正如 call 所期望的那样。当您从对象调用函数时,this 将引用 那个对象,当你在某个函数上调用call 时会发生这种情况(函数也是对象)。

【讨论】:

    【解决方案3】:

    我在这个问题上苦苦挣扎,因为在精神上我很难从“this”这个参数中跳出来,然后将其视为一种伪对象,从中调用 call 方法。我总结了我的理解(在 Nodejs 中):

    function f(a) {console.log(a);}
    
    f.call(global, 1);
    //function reference to be called = f
    //context to call it from = global
    //parameters used when invoking = 1
    //equivalent call => global.f(1)
    
    Function.prototype.call.call(f, global, 1);
    //function reference to be called = Function.prototype.call
    //context to call it from = f
    //parameters used when invoking = global, 1
    //equivalent call => f.call(global, 1)
    

    参考文献:

    A shorthand for Function.prototype.call.call?

    Javascript Function.prototype.call()

    Can't use Function.prototype.call directly

    a is a function, then what `a.call.call` really do?

    【讨论】:

      猜你喜欢
      • 2021-12-03
      • 1970-01-01
      • 1970-01-01
      • 2014-06-22
      • 2018-11-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-06
      • 2013-08-07
      相关资源
      最近更新 更多