在方法

时间:2015-10-05 07:58:26

标签: javascript ecmascript-2015

我试图弄清楚我在Node v4.1.1(V8 v4.5.103.33)中看到的关于super和箭头函数的某些行为是指定的行为,如果是这样(或者实际上,如果没有的话), specification中的 它应该(或不应该)在我所拥有的各种情况下工作。

简而言之:在方法内的另一个箭头函数(super)内的箭头函数(inner)中使用outer可以,除非 {{1}即使outer引用inner的参数或变量,也有参数或变量inner引用。我想知道规范说什么:它是否应该一直有效,即使V8失败了?没有时间?只有在V8目前让它工作的特定情况下,而不是它不在的地方?

这是一个MCVE:

method

失败了:

$ node test.js
/path/test.js:13
                super.show(`arg = ${arg}, x = ${x}`);
                ^^^^^

SyntaxError: 'super' keyword unexpected here
    at outer (/path/test.js:16:13)
    at Child.method (/path/test.js:18:9)
    at Object. (/path/test.js:22:13)
    at Module._compile (module.js:434:26)
    at Object.Module._extensions..js (module.js:452:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:475:10)
    at startup (node.js:117:18)
    at node.js:951:3

如果您删除了对"use strict"; class Parent { show(msg) { console.log(`Parent#show: ${msg}`); } } class Child extends Parent { method(arg) { let outer = (x) => { console.log(`outer: x = ${x}`); let inner = () => { super.show(`arg = ${arg}, x = ${x}`); }; inner(); }; outer(42); } } new Child().method("arg"); 中的x的引用:

inner

工作并输出:

outer: x = 42
Parent#show: arg = arg

为了向自己证明“工作”案例不是函数被优化掉了,我将它们退出方法并调用它们。这是一个稍微复杂的案例(请注意评论);此版本有效

            let inner = () => {
                super.show(`arg = ${arg}`); // <== removed x from this
            };

输出:

method called with arg, flag is false
type of outer2: function
type of inner2: function
Parent2#show: A: arg (false)

但是,如果我们注释掉标记为"use strict"; class Parent2 { show(msg) { console.log(`Parent2#show: ${msg}`); } } class Child2 extends Parent2 { method(arg) { let flag = Math.random() < 0.5; console.log(`method called with ${arg}, flag is ${flag}`); let x = "A"; // **A** let outer2 = (/*x*/) => { // **B** //let x = "C"; // **C** let inner2 = () => { super.show(`${x}: ${arg} (${flag})`); }; return inner2; }; return outer2; } } let o = new Child2().method("arg"); console.log(`type of outer2: ${typeof o}`); let i = o(); console.log(`type of inner2: ${typeof i}`); i("B"); 的行并取消注释AB,则会像MCVE一样失败。

更多说明:

  • 我应该强调你需要有箭头函数嵌套C可以轻松访问outer。我不想用另一个大代码块来混淆问题,但是如果你在super的顶部添加super.show(`outer: arg = ${arg}, x = ${x}`);,它就可以了。

  • 正如您所看到的,outer同时使用inner中的参数和变量(嗯,MCVE只使用了一个arg),这很好,但只要method inner 1}}尝试使用outer中的参数或变量,事情就会爆炸。

  • Babel和Traceur都非常乐意传达V8无法运行的情况(herehere),但这可能只是他们弄错了V8正确(当然,反之亦然)。

  • 它与模板字符串无关; MCVE的前版本没有使用它们(并且确实使用了promises,这就是我们最终用箭头指向箭头的方式)。

只是要强调,问题是这里指定的行为是什么,而指定的是指定

我的直觉告诉我这只是一个V8错误 - 毕竟,这个东西是早期的,公平的'nuff。但无论哪种方式,我只是想弄清楚应该是的行为,规范是什么。我试图跟随其各种各样的部分谈论super和“基础对象”等等,坦率地说,我只是没有得到它。

1 个答案:

答案 0 :(得分:9)

看来这确实是V8中的一个错误(它现在已经是fixed)。请注意,如果没有嵌套箭头功能,它可以正常工作。

因此,如果我们要查看文字规范文本以查看这是否是错误,那么让我们从super关键字本身开始:

  

12.3.5.3 Runtime Semantics: MakeSuperPropertyReference(propertyKey, strict)

     

使用参数propertyKey和strict的抽象操作MakeSuperPropertyReference执行以下步骤:

     
      
  1. 让我们成为GetThisEnvironment()。
  2.   
  3. 如果env.HasSuperBinding()为false,则抛出ReferenceError异常。
  4.   
  5. 让actualThis为env.GetThisBinding()。
  6.   
  7. ReturnIfAbrupt(actualThis)。
  8.   
  9. 让baseValue为env.GetSuperBase()。
  10.   
  11. 让bv为RequireObjectCoercible(baseValue)。
  12.   
  13. ReturnIfAbrupt(BV)。
  14.   
  15. 返回类型为Reference的值,该类型为超级引用,其基值为bv,其引用名称为propertyKey,其thisValue为actualThis,其严格引用标志为strict。
  16.   

让我们忽略大多数罗嗦的东西并担心GetThisEnvironment():

  

8.3.2 GetThisEnvironment ( )

     

抽象操作GetThisEnvironment找到当前提供关键字this绑定的环境记录。 GetThisEnvironment执行以下步骤:

     
      
  1. 让lex成为​​正在运行的执行上下文的LexicalEnvironment。
  2.   
  3. 重复
      一个。让envRec成为lex的EnvironmentRecord   湾让存在为envRec.HasThisBinding()。
      C。如果exists为true,则返回envRec   d。设外为lex的外部环境参考值   即让lex成为​​外在的。
  4.         

    注意步骤2中的循环将始终终止,因为环境列表始终以具有此绑定的全局环境结束。

现在我们知道箭头函数没有绑定到this,它应该跳过当前函数的环境记录,并且函数会立即将它包围起来。

一旦到达“常规”函数,这将停止,并按照规范继续按预期检索对super对象的引用。

ECMAScript规范的项目编辑Allen Wirfs-Brock似乎证实这是在几年前es-discuss mailing list的回复中的意图:

  

super在词法范围内,就像this到最近的定义它的封闭函数一样。除了箭头函数之外的所有函数定义表单都引入了新的this / super绑定,因此我们可以[说] this / super根据最接近的非箭头绑定功能定义。