ES6 到 ES5:Babel 对 class 扩展的实现

ES6 to ES5: Babel's implementation of class extension

由于旧浏览器的支持,我们都使用babeljs将ES6转译为ES5。当 babel 编译一个从另一个 class 扩展而来的 class 时。编译代码的一部分是类似这样的:

...
if (superClass)
    Object.setPrototypeOf
      ? Object.setPrototypeOf(subClass, superClass)
      : (subClass.__proto__ = superClass);
...

顶部代码块用于从 parent class 扩展静态属性。他们用Object.setPrototypeOf改变了childclass的[[Prototype]]。不要混淆 .prototype[[Prototype]] 是完全不同的东西。

MDN 在其参考中关于使用 Object.setPrototypeOf 的内容如下:

Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine.

我的问题出现在这里:如果我们可以用另一种方式达到相同的结果,为什么 Babel 使用 Object.setPrototypeOf?我试图通过循环构造函数 object.

从 parent class (我之前分配给它)复制所有静态方法
...
var parentStaticProps = parentClass.prototype.constructor;

for (var prop in parentStaticProps) {
  childClass.prototype.constructor[prop] = parentStaticProps[prop];
}
...

而且比babel的实现速度还快!我创建了一些类似于 babel 扩展 class 并在 jsPerf 中测试它的东西。 我的前 5 名测试 运行 结果非常令人失望 Object.setPrototypeOf:慢 19%,慢 20%,慢三倍 21%。

我知道 Object.setPrototypeOf 可能需要使用肯定有一些原因。我想知道。如果是关于 non-enumerable 属性那么我们肯定可以使用一些其他方法。

If we can achieve the same result with another way why Babel uses Object.setPrototypeOf?

很简单,因为没有其他方法可以达到相同的结果。仅仅复制所有属性的当前值与从另一个对象动态继承是不同的。 Babel 的目标是正确,而不是快速。

And it is also faster than babel's implementation! I have created something similar what babel does to extend a class and tested it in jsPerf.

好吧,创建和扩展 类 慢,但这并不重要。 使用 那些 类 - 例如访问静态属性 - 是这里的重要方面。通常没有问题的是你改变一个新的(尚未使用的)对象的 [[prototype]] - 警告更关心在对象生命周期的中间进行突变。