我试图弄清楚我在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");
的行并取消注释A
或B
,则会像MCVE一样失败。
更多说明:
我应该强调你需要有箭头函数嵌套。 C
可以轻松访问outer
。我不想用另一个大代码块来混淆问题,但是如果你在super
的顶部添加super.show(`outer: arg = ${arg}, x = ${x}`);
,它就可以了。
正如您所看到的,outer
同时使用inner
中的参数和变量(嗯,MCVE只使用了一个arg),这很好,但只要method
inner
1}}尝试使用outer
中的参数或变量,事情就会爆炸。
Babel和Traceur都非常乐意传达V8无法运行的情况(here和here),但这可能只是他们弄错了V8正确(当然,反之亦然)。
它与模板字符串无关; MCVE的前版本没有使用它们(并且确实使用了promises,这就是我们最终用箭头指向箭头的方式)。
只是要强调,问题是这里指定的行为是什么,而指定的是指定。
我的直觉告诉我这只是一个V8错误 - 毕竟,这个东西是早期的,公平的'nuff。但无论哪种方式,我只是想弄清楚应该是的行为,规范是什么。我试图跟随其各种各样的部分谈论super
和“基础对象”等等,坦率地说,我只是没有得到它。
答案 0 :(得分:9)
看来这确实是V8中的一个错误(它现在已经是fixed)。请注意,如果没有嵌套箭头功能,它可以正常工作。
因此,如果我们要查看文字规范文本以查看这是否是错误,那么让我们从super
关键字本身开始:
12.3.5.3 Runtime Semantics: MakeSuperPropertyReference(propertyKey, strict)
使用参数propertyKey和strict的抽象操作MakeSuperPropertyReference执行以下步骤:
- 让我们成为GetThisEnvironment()。
- 如果env.HasSuperBinding()为false,则抛出ReferenceError异常。
- 让actualThis为env.GetThisBinding()。
- ReturnIfAbrupt(actualThis)。
- 让baseValue为env.GetSuperBase()。
- 让bv为RequireObjectCoercible(baseValue)。
- ReturnIfAbrupt(BV)。
- 返回类型为Reference的值,该类型为超级引用,其基值为bv,其引用名称为propertyKey,其thisValue为actualThis,其严格引用标志为strict。
醇>
让我们忽略大多数罗嗦的东西并担心GetThisEnvironment():
8.3.2 GetThisEnvironment ( )
抽象操作GetThisEnvironment找到当前提供关键字this绑定的环境记录。 GetThisEnvironment执行以下步骤:
- 让lex成为正在运行的执行上下文的LexicalEnvironment。
- 重复
醇>
一个。让envRec成为lex的EnvironmentRecord 湾让存在为envRec.HasThisBinding()。
C。如果exists为true,则返回envRec d。设外为lex的外部环境参考值 即让lex成为外在的。注意步骤2中的循环将始终终止,因为环境列表始终以具有此绑定的全局环境结束。
现在我们知道箭头函数没有绑定到this
,它应该跳过当前函数的环境记录,并且函数会立即将它包围起来。
一旦到达“常规”函数,这将停止,并按照规范继续按预期检索对super
对象的引用。
ECMAScript规范的项目编辑Allen Wirfs-Brock似乎证实这是在几年前es-discuss mailing list的回复中的意图:
super
在词法范围内,就像this
到最近的定义它的封闭函数一样。除了箭头函数之外的所有函数定义表单都引入了新的this
/super
绑定,因此我们可以[说]this
/super
根据最接近的非箭头绑定功能定义。