首页 > 解决方案 > Function.prototype.call.call 的 ECMAScript 算法

问题描述

众所周知的Function.prototype.call.call(func)是一样Function.call.call(func)的,一样的func()

这种技术从第一个浏览器开始就一直在起作用。因此,“双重调用”的算法可以很容易地从ES5 规范中推导出来

Function.prototype.call (thisArg [ , arg1 [ , arg2, ... ] ] )
当在一个带有参数thisArg和可选参数arg1、arg2等的对象func上调用 call方法时,将执行以下步骤:

  1. 如果IsCallable ( func ) 为 false,则抛出TypeError异常。
  2. argList为空List
  3. 如果使用多个参数调用此方法,则以从arg1开始的从左到右的顺序将每个参数附加为argList的最后一个元素
  4. 返回调用func的[[Call]]内部方法的结果,提供thisArg作为this值,提供argList作为参数列表。

考虑一个简单的例子:

var func = function(){ console.log(42) };
Function.prototype.call.call(func);

提供.call.call(func)的内部方法 [[Call]] 的调用也是如此。这怎么会导致调用?Function.prototype.callfuncfunc

看起来 [[Call]] 的内部方法.call实际上调用了它的this值(如func.call())。但我没有从规范中得到它。

标签: javascriptecmascript-6ecmascript-5

解决方案


[[Call]]是所有函数对象的内部方法。它通过设置上下文和评估函数体来执行实际的函数调用。

来自ES5 规范

13.2.1[[Call]]

当使用 this 值和参数列表调用Function 对象F[[Call]]的内部方法时,将执行以下步骤:

  1. 假设funcCtx是使用F[[FormalParameters]]内部属性的值、传递的参数 List args 和this值(如 10.4.3 中所述)为函数代码建立新执行上下文的结果。
  2. 让 result 是评估FunctionBody的结果,它是F[[Code]]内部属性的值。如果F没有[[Code]]内部属性或其值为空 FunctionBody,则结果(normal, undefined, empty)
  3. 退出执行上下文funcCtx,恢复之前的执行上下文。
  4. 如果result .type 被抛出,则抛出result .value。
  5. 如果返回结果.type,则返回结果.value。
  6. 否则结果.type 必须是正常的。返回未定义

但是,请注意函数调用语法[[Call]]也使用了它fn()(查看第 8 步):

11.2.3 函数调用

产生 式 CallExpression : MemberExpression Arguments 的评估如下:

  1. ref 是评估 MemberExpression的结果。
  2. func 为 GetValue( ref )。
  3. argList 为评估 Arguments的结果,生成参数值的内部列表(参见 11.2.4)。
  4. 如果 Type( func ) 不是 Object,则抛出 TypeError 异常。
  5. 如果 IsCallable 为 false,则抛出 TypeError 异常。
  6. 如果 Type( ref ) 是 Reference,那么
    1. 如果 IsPropertyReference( ref ) 为 true,则
      1. thisValue 为 GetBase( ref )。
    2. 否则, ref的基础 是环境记录
      1. thisValue 成为调用 GetBase( ref ) 的 ImplicitThisValue 具体方法的结果。
  7. 否则,Type( ref ) 不是参考。
    1. thisValueundefined
  8. 返回调用func[[Call]]的内部方法 的结果,提供 thisValue 作为 this 值,并提供列表 argList 作为参数值。

产生 式 CallExpression : CallExpression Arguments 的评估方式完全相同,除了包含的 CallExpression 在步骤 1 中评估。


推荐阅读