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

7
今天早上我看到了Š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;赋值给B的原型链也可以起到同样的作用。因此我想我可以使用Object.create来实现相同的效果:
let A = {
  run() {
    console.log('A runs');
  }
};

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

很不幸,这会导致Firefox和Chrome都出现错误:

SyntaxError: use of super property accesses only valid within methods or eval code within methods

并且在尝试将属性描述符对象传递给Object.create的第二个参数时也会发生同样的情况。
从语义上讲,它们在我的看法中似乎是相等的,所以我不太确定发生了什么(是因为对象文字吗?)。
现在我想知道,这是确切定义的标准行为吗(赞赏规范参考)?Object.create是否有一些缺失的实现,或者首先对象文字就不能正常工作?

好的,你的情况下 BA 的一个实例(好的,我知道这个词在 JS 上下文中不应该使用)。所以 B.run();A.run(); 是一样的吗? - Arg0n
请查看此推文,这意味着super只能从简短定义方法中使用。 - Dmitri Pavlutin
3个回答

8

Allen Wirfs-Brock,ES2015规范的编辑,非常友善地在Twitter上回答了我的问题

为什么这是一个错误?

超级属性引用只能出现在类定义或对象字面量中的“简洁方法”中 http://tc39.github.io/ecma262/#sec-function-definitions-static-semantics-early-errors

在规范的这个部分,静态语义:早期错误,有四个点似乎与此相关:

  • 如果FormalParameters包含SuperProperty,则会导致语法错误。
  • 如果FunctionBody包含SuperProperty,则会导致语法错误。
  • 如果FormalParameters包含SuperCall,则会导致语法错误。
  • 如果FunctionBody包含SuperCall,则会导致语法错误。

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

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

原因是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(closure, object)。

这些语义将来可能会改变吗?

后向链接很重要。考虑了动态设置它的方式,可能会再次考虑。由于容易出错,因此被省略了。

因此,将来可能会引入类似于 .toMethod 的东西,可以将对象super引用设置为可行,从而使我最初使用 Object.create 的例子成为可能。


1

I mean you can do this:

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

By the way, this also fails:

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

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

Object.setPrototypeOf(B, A);

B.run();

2
你的回答第一部分似乎不相关,第二部分更有趣。也许它确实必须是在以下定义的方法定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Method_definitions - nils

1
您的回答已经涵盖了大部分内容。super调用仅限于方法中,因为方法与声明它们的对象紧密相关。也就是说,您最好的选择是执行。
let B = {
    __proto__: A,
    run(){
        super.run();
    }
};

以声明式的方式创建一个对象,使其原型为A,而不需要调用可能会导致性能下降的Object.setPrototypeOf(B, A)


谢谢您的澄清,我以某种方式认为这个概念也会降低效率。所以只要它发生在对象的初始定义内,现代引擎仍然会进行优化吗? - nils
据我所知,是的,但它需要一个支持__proto__的引擎,因此在IE<=10上无法工作。不过,如果您需要支持ES5环境,您可能可以将https://www.npmjs.com/package/babel-plugin-proto-to-create移植到Babel 6(它是Babel 5)。 - loganfsmyth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接