在使用Object.create创建的对象中使用super

时间:2016-05-12 14:06:22

标签: javascript ecmascript-6

今天早上我遇到了tweet from Šime Vidas,他提出了在对象文字中使用super的以下可能性:

let A = {
  run() {
    console.log('A runs');
  }
};

let B = {
  run() {
    super.run();
  }
};

Object.setPrototypeOf(B, A);

B.run(); // A runs

这很有效,而且在Firefox和Chrome中分配B.__proto__ = A;似乎也可以正常工作。

所以我想我可以对Object.create

做同样的事情
let A = {
  run() {
    console.log('A runs');
  }
};

let B = Object.create(A);
B.run = function() { super.run() };

不幸的是,这导致两个Firefox都出错:

  

SyntaxError:使用超级属性访问仅在方法或方法中的eval代码中有效

和Chrome:

  

未捕获的SyntaxError:'super'关键字在这里意外

当我尝试将属性描述符对象传递给Object.create的第二个参数时,会发生同样的情况。

在语义上,它们似乎都与我相等,所以我不太确定发生了什么(是因为对象字面意思?)。

现在我想知道,这个标准行为是否准确定义(规格参考赞赏)? Object.create是否缺少某些实现,或者对象文字是否应该首先起作用?

3 个答案:

答案 0 :(得分:5)

ES2015规范的编辑Allen Wirfs-Brock非常友好answer my question on twitter

为什么这是一个错误?

  

超级属性引用只能出现在类def或obj lit http://tc39.github.io/ecma262/#sec-function-definitions-static-semantics-early-errors

类中的“简明方法”中

在规范的那一部分,静态语义:早期错误,有四点似乎是相关的:

  • 如果FormalParameters包含SuperProperty,则为语法错误。
  • 如果FunctionBody包含SuperProperty,则为语法错误。
  • 如果FormalParameters包含SuperCall,则为语法错误。
  • 如果FunctionBody包含SuperCall为true,则为语法错误。

因此,超级属性调用既不允许在函数参数中,也不在常规函数体中。因此我的问题。

为什么需要使用方法定义?

  

原因是super需要从方法到其包含对象的反向链接。 http://tc39.github.io/ecma262/#sec-runtime-semantics-definemethod

意思是,如果我在方法中有超级调用,并且我将该方法分配给另一个变量,它仍然必须工作:

let B = {
  run() {
    super.run();
  },
  walk() {
    console.log(typeof this.run);
  }
};

var run = B.run;
run(); // the 'super' binding still works, thanks to the internal MakeMethod

var walk = B.walk;
walk(); // 'undefined': the 'this' binding on the other hand is lost, as usual

这是在定义方法语义的部分的第7步中指定的,当前对于对象的常规赋值不会发生这种情况:

  1. 执行 MakeMethod (闭包,对象)。
  2. 这些语义将来会改变吗?

      

    反向链接至关重要。动态设置它的方法被考虑了,可能会再次出现。遗漏了b / c错误

    所以有可能像.toMethod这样可以设置对象super引用的东西可能会被引入到语言中,这使得我的初始示例可以Object.create成为可能

答案 1 :(得分:1)

我的意思是你可以这样做:

let A = {
  run() {
    console.log('A runs');
  }
};

let B = {
  run() {
    super.run();
  }
};

Object.setPrototypeOf(B, A);

let C = Object.create(B);
C.run(); //A runs

顺便说一句,这也失败了:

let A = {
  run() {
    console.log('A runs');
  }
};

let B = {
  run: function() {
    super.run(); // 'super' keyword unexpected here
  }
};

Object.setPrototypeOf(B, A);

B.run();

答案 2 :(得分:1)

你的答案涵盖了大部分内容。 super调用仅允许在方法中使用,因为方法与声明它们的对象紧密相关。那就是说,你最好的选择就是

let B = {
    __proto__: A,
    run(){
        super.run();
    }
};

以声明方式创建一个以A为原型的对象,而不需要对Object.setPrototypeOf(B, A).进行潜在的去优化调用