在方法中的箭头函数中的箭头函数中使用' super '

Using `super` within an arrow function within an arrow function within a method

本文关键字:函数 super 方法      更新时间:2023-09-26

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

简而言之:在方法中的箭头函数(inner)中使用super在另一个箭头函数(outer)中工作除非 outerinner引用的参数或变量,即使inner引用了method的参数或变量。我想知道规范说关于这一点:它应该一直工作,即使V8是失败的?没有时间?只有在V8目前允许它工作的特定情况下,而不是在它不允许的情况下?

这是一个MCVE:

"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");

<>之前$ node test.js/道路/. js: 13超级。Show (' arg = ${arg}, x = ${x} ');^ ^ ^ ^ ^SyntaxError: 'super'关键字这里是意外的(/path/test.js:16:13)在孩子。方法(/道路/. js:到了在对象。(/道路/. js:二二13)在模块。_compile (module.js 434:26):at Object.Module._extensions. js (module.js:452:10)在模块。负载(module.js 355:32):在Function.Module。_load (module.js 310:12):at Function.Module.runMain (module.js:475:10)启动时(node.js:117:18)在node . js: 951:3之前

如果你删除x的引用,在inner:

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

it 工作并输出:

<>之前外部:x = 42Parent#show: arg = arg之前

为了向自己证明"工作"的情况不是函数被优化掉了,我从方法中返回它们并调用它们。下面是稍微复杂一点的情况(请注意注释);这个版本工作:

"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");
输出:

<>之前方法调用时,标志为falseouter2的类型:功能inner2的类型:函数Parent2#show: A: arg (false)之前

但是如果我们注释掉标记为A的行,并取消BC的注释,它就会像MCVE一样失败。

更指出:

  • 我要强调的是,您需要嵌套箭头函数outer访问super没有问题。我不想用另一个大的代码块混淆问题,但是如果你在outer的顶部添加super.show(`outer: arg = ${arg}, x = ${x}`);,它工作得很好。

  • 正如您所看到的,inner同时使用method的参数和变量(好吧,MCVE只是使用arg),这很好,但是一旦inner试图使用outer的参数或变量,事情就会爆发。

  • Babel和Traceur都非常乐意翻译V8无法运行的情况(这里和这里),但这可能只是他们得到V8正确的错误(或者,当然,反之亦然)。

  • 与模板字符串无关;这个的pre-MCVE版本没有使用它们(并且使用了承诺,这就是为什么我们最终使用箭头中的箭头)。

只是强调一下,问题是这里指定的行为是什么,以及在规范中指定的位置

我的直觉告诉我这只是一个V8的bug —毕竟,这种东西现在还为时尚早。但不管怎样,我只是想弄清楚行为应该是什么,规范是怎么说的。我试着跟随它的各种各样的章节谈论super和"基本对象"等,坦率地说,我只是不明白它。

看来这确实是V8中的一个bug(现在已经修复了)。请注意,如果没有嵌套的箭头函数,它可以正常工作。

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

12.3.5.3运行时语义:MakeSuperPropertyReference(propertyKey, strict)

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

  1. 让环境为GetThisEnvironment().
  2. 如果env.HasSuperBinding()为false,抛出ReferenceError异常。
  3. 让actualThis为env.GetThisBinding()。
  4. ReturnIfAbrupt (actualThis)。
  5. 让baseValue为env.GetSuperBase().
  6. 让bv为RequireObjectCoercible(baseValue)
  7. ReturnIfAbrupt (bv)。
  8. 返回一个Reference类型的值,该值为Super Reference,基值为bv,引用名称为propertyKey, thisValue为actualThis,严格引用标志为strict。

让我们忽略大多数冗长的东西,并关注GetThisEnvironment():

8.3.2 GetThisEnvironment ()

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

  1. 让lex作为正在运行的执行上下文的LexicalEnvironment。
  2. 重复
    a.让envRec成为lex的环境记录。
    b.让存在为envRec.HasThisBinding()。
    c.如果exists为true,返回envRec。
    d.设outer为lex的外部环境引用值。
    e.让lex在外部。

步骤2中的循环将总是终止,因为环境列表总是以具有this绑定的全局环境结束。

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

这将在到达"常规"函数,然后按照规范检索对super对象的引用。

Allen Wirfs-Brock, ECMAScript规范的项目编辑,似乎在几年前es-discuss邮件列表的回复中证实了这一点:

super具有词法作用域,就像this一样,限定在定义它的最近的封闭函数内。除了箭头函数外,所有的函数定义形式都引入了新的this/super绑定,所以我们可以[说]this/super绑定是根据最近的封闭的非箭头函数定义。