在方法内的箭头函数内的箭头函数中使用`super` [英] Using `super` within an arrow function within an arrow function within a method

查看:244
本文介绍了在方法内的箭头函数内的箭头函数中使用`super`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想弄清楚在节点v4.1.1(V8 v4.5.103.33)中有关 super 和箭头函数的某些行为是否为指定的行为,如果是(或者如果没有),那么在 -262 / 6.0 / index.html>规范,它表示应该(或不应该)在各种情况下工作。



简而言之在另一个箭头函数( outer 中)的箭头函数( inner )中使用 super 一个方法中的code>)工作,除非 outer 具有参数或变量 inner 引用,即使 inner 引用方法的参数或变量。我想知道规范说明:即使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
/path/test.js:13
super.show(`arg = $ {arg},x = $ {x}`);
^^^^

SyntaxError:'super'关键字意外在这里
在外面(/path/test.js:16:13)
在Child。方法(/path/test.js:18:9)
在Object。 (/path/test.js:22:13)
在Module._compile(module.js:434:26)
在Object.Module._extensions..js(module.js:452:10 )
在Module.load(module.js:355:32)
在Function.Module._load(module.js:310:12)
在Function.Module.runMain(模块。 js:475:10)
在启动时(node.js:117:18)
在node.js:951:3

如果您删除 x 内部的引用:

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

工作并输出:

 
outer:x = 42
父#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(`方法调用$ {arg},标志是$ {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);

输出:

 
方法调用arg,flag为false
类型的outer2:function
type of inner2:function
Parent2#show:A:arg(false)

但是,如果我们注释了标有 A 的行,并取消注释$ code> B C ,它像MCVE那样失败。



更多注释:




  • 我应该强调,您需要使用嵌套的箭头功能。 outer 无论访问 super 是没有问题的。我不想用另一个大的代码块来混淆这个问题,但是如果添加 super.show(`outer:arg = $ {arg},x = $ {x}`); 外部的顶部,它的工作原理很好。


  • 可以看到, inner 使用方法中的一个参数和一个变量(好吧,MCVE只是使用一个arg)这很好,但是一旦 inner 尝试使用 outer 中的参数或变量,就会爆炸。 / p>


  • Babel和Traceur都很高兴透露V8不会运行的情况(这里这里),但这可能只是他们弄错了那V8是正确的(或当然反之亦然)。


  • 它与模板字符串无关;它的前MCVE版本没有使用它们(并且使用承诺,这是我们如何用箭头内的箭头)。




只是要强调,问题是这里指定的行为在规范中指定的



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

解决方案

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

所以如果我们来看看文字规范文本,看看这是否是一个错误,那么我们先从 super 关键字本身:


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



抽象操作MakeSuperPropertyReference带有参数propertyKey和strict执行followi如果env.HasSuperBinding()是...,那么你可以使用env来获取GetThisEnvironment()。


    <假的,抛出一个ReferenceError异常。
  1. 让actualThis be env.GetThisBinding()。

  2. ReturnIfAbrupt(actualThis)。

  3. / li>
  4. 返回值(bv)。 li>
  5. 返回一个类型为Reference的值,它是一个基准值为bv的超级引用,其引用名称为propertyKey,其ValueValue为actualThis,其严格引用标志是严格的。


让我们忽略大部分的冗长的东西,担心GetThisEnvironment():


8.3.2 GetThisEnvironment()



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


  1. 让lex成为​​正在运行的执行环境的LexicalEnvironment。

  2. 重复< br>
    a。让envRec成为lex的EnvironmentRecord。

    b。让存在envRec.HasThisBinding()。

    c。如果存在,则返回envRec。

    d。让外部是lex的外部环境参考值。

    e。让lex在外面。

注意步骤2中的循环将永远终止,因为环境列表总是以全局环境结束这个绑定。


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



一旦达到常规功能,它将停止,然后继续根据规范检索对 super 对象的引用。



Allen Wirfs-Brock,project ECMAScript规范的编辑似乎确认这是在几年前讨论邮件列表


super 是词法的范围,就像这个 t o定义它的最接近的函数。除了箭头功能之外的所有功能定义表格都引入了新的这个 / super bindings,所以我们可以[说] 这个 / super 根据最接近的非箭头函数定义进行绑定。



I'm trying to figure out whether some behavior I'm seeing in Node v4.1.1 (V8 v4.5.103.33) regarding super and arrow functions is specified behavior, and if so (or indeed, if not), where it is in the specification that it says it should (or should not) work in the various cases I have.

In brief: Using super in an arrow function (inner) inside another arrow function (outer) inside a method works unless outer has arguments or variables inner references, even if inner references arguments or variables of method. I want to know what the spec says about that: Should it work all the time, even where V8 is failing? None of the time? Only in the specific cases where V8 is currently letting it work, and not where it isn't?

Here's an 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");

That fails with:

$ 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

If you remove the reference to x that's in inner:

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

it works and outputs:

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

To prove to myself that the "works" case wasn't that the functions were being optimized away, I returned them out of the method and called them. Here's that slightly-more complex case (note the comments); this version works:

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

Output:

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

But if we comment out the line labelled A and uncomment either B or C, it fails like the MCVE does.

More notes:

  • I should emphasize that you need to have the arrow functions nested. outer has no trouble accessing super. I don't want to clutter up the question with another big code block, but if you add super.show(`outer: arg = ${arg}, x = ${x}`); at the top of outer, it works just fine.

  • As you can see, inner uses both an argument and a variable from method (well, the MCVE just uses an arg), and that's fine, but as soon as inner tries to use an argument or variable from outer, things blow up.

  • Babel and Traceur are both perfectly happy to transpile the case that V8 won't run (here and here), but that could just be them getting something wrong that V8 gets right (or, of course, vice-versa).

  • It doesn't relate to template strings; the pre-MCVE version of this didn't use them (and did use promises, which is how we ended up with arrows inside arrows).

Just to emphasize, the question is what's the specified behavior here, and where in the spec is it specified.

My gut tells me this is just a V8 bug — it's early days for this stuff, after all, fair 'nuff. But either way, I'm just trying to figure out what the behavior should be, what the spec says. I've tried to follow its various and sundry sections talking about super and "base objects" and such, and frankly I'm just not getting it.

解决方案

It appears that this is indeed a bug in V8 (it has now been fixed). Note that if there isn't the nested arrow function, it works fine.

So if we're going to take a look through the literal specification text to see whether this is a bug, let's start with the super keyword itself:

12.3.5.3 Runtime Semantics: MakeSuperPropertyReference(propertyKey, strict)

The abstract operation MakeSuperPropertyReference with arguments propertyKey and strict performs the following steps:

  1. Let env be GetThisEnvironment( ).
  2. If env.HasSuperBinding() is false, throw a ReferenceError exception.
  3. Let actualThis be env.GetThisBinding().
  4. ReturnIfAbrupt(actualThis).
  5. Let baseValue be env.GetSuperBase().
  6. Let bv be RequireObjectCoercible(baseValue).
  7. ReturnIfAbrupt(bv).
  8. Return a value of type Reference that is a Super Reference whose base value is bv, whose referenced name is propertyKey, whose thisValue is actualThis, and whose strict reference flag is strict.

Let's ignore most of the wordy stuff and worry about GetThisEnvironment():

8.3.2 GetThisEnvironment ( )

The abstract operation GetThisEnvironment finds the Environment Record that currently supplies the binding of the keyword this. GetThisEnvironment performs the following steps:

  1. Let lex be the running execution context’s LexicalEnvironment.
  2. Repeat
    a. Let envRec be lex’s EnvironmentRecord.
    b. Let exists be envRec.HasThisBinding().
    c. If exists is true, return envRec.
    d. Let outer be the value of lex’s outer environment reference.
    e. Let lex be outer.

NOTE The loop in step 2 will always terminate because the list of environments always ends with the global environment which has a this binding.

Now as we know that arrow functions don't have bindings to this, it should skip the environment record of the current function and the function immediately enclosing it.

This will stop once reaching the "regular" functions and go on to retrieve the reference to the super object as expected, according to the specification.

Allen Wirfs-Brock, project editor of the ECMAScript specification, seems to confirm this was intended in a reply on the es-discuss mailing list a few years back:

super is lexically scoped, just like this to the closest enclosing function that defines it. All function definition forms except for arrow functions introduce new this/super bindings so we can just [say] that this/super binds according to the closest enclosing non-arrow function definition.

这篇关于在方法内的箭头函数内的箭头函数中使用`super`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆