【问题标题】:Using super in an object created with Object.create在使用 Object.create 创建的对象中使用 super
【发布时间】:2016-05-12 14:06:22
【问题描述】:

今天早上我遇到了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

这可行,而分配B.__proto__ = A; 似乎也可以在 Firefox 和 Chrome 中使用。

所以我想我可以对 Object.create 做同样的事情:

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

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

很遗憾,这会导致 Firefox 出现错误:

SyntaxError:使用超属性访问仅在方法内有效或在方法内评估代码

和铬:

未捕获的 SyntaxError: 'super' 关键字在此处意外

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

在语义上,它们似乎都和我一样,所以我不太确定发生了什么(是因为对象字面量吗?)。

现在我想知道,这种标准行为是否已完全定义(赞赏规范参考)? Object.create 是否缺少一些实现,或者对象字面量是否应该首先不起作用?

【问题讨论】:

  • 好吧,在你的情况下,BAinstance(好吧,我知道这个词不应该在 JS 上下文中使用)。那么B.run();A.run(); 一样吗?
  • 勾选this tweet这意味着super只能用于shortand定义方法。

标签: javascript ecmascript-6


【解决方案1】:

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

为什么会出现错误?

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

在规范的那个部分,Static Semantics: Early Errors,有四点似乎是相关的:

  • 如果 FormalParameters 包含 SuperProperty 为 true,则为语法错误。
  • 如果 FunctionBody 包含 SuperProperty 为 true,则会出现语法错误。
  • 如果 FormalParameters 包含 SuperCall 为 true,则为语法错误。
  • 如果 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(闭包,对象)。

这些语义将来会改变吗?

反向链接是必不可少的。动态设置它的方法被考虑并且可能再次被考虑。遗漏 b/c 容易出错

所以有可能像.toMethod 这样可以设置对象super 引用的东西毕竟可以被引入到语言中,从而使我最初使用Object.create 的例子成为可能。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    【解决方案3】:

    您的回答涵盖了大部分内容。 super 调用只允许在方法中使用,因为方法与声明它们的对象密切相关。也就是说,您最好的选择是这样做

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

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

    【讨论】:

    • 感谢您的澄清,我不知何故认为这个概念也会贬低。所以只要它发生在对象的初始定义中,它仍然被现代引擎优化?
    • 据我所知,是的,但它确实需要支持__proto__ 的引擎,因此它无法在 IEnpmjs.com/package/babel-plugin-proto-to-create 移植到 Babel 6(它是 Babel 5)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多