javascriptecmascript-6babeljsecmascript-5

Why does Babel modify prototypes of subclasses instead of copying static properties over?


Due to old browser support, we all use babeljs to transpile ES6 into ES5. When Babel compiles a class that is extended from another class. A part of compiled code is something similar to this:

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

The top code block is used for extending static properties from the parent class. They have used Object.setPrototypeOf to change the [[Prototype]] of the child class. It is not to confuse that .prototype and [[Prototype]] is a completely separate things.

What MDN says in its reference regarding the use of Object.setPrototypeOf is below:

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.

My question emerges here: if we can achieve the same result in another way why Babel uses Object.setPrototypeOf? I have tried to copy all the static methods from the parent class (which I have previously assigned to that) by looping through the constructor Function object.

...
var parentStaticProps = parentClass.prototype.constructor;

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

And it is also faster than Babel's implementation! I have created something similar to what Babel does to extend a class and tested it in jsPerf. My top 5 test run result was very disappointing with Object.setPrototypeOf: 19% slower, 20 % slower, and three times 21% slower.

I know there must have some reasons why Object.setPrototypeOf might need to use. I want to know. If it is about non-enumerable properties then we can definitely use some other methods.


Solution

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

    Simply because there is no other way that achieves the same result. Just copying over the current values of all properties is not the same as dynamically inheriting from another object. Babel aims to be correct, not fast.

    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.

    Well, creating and extending classes is slow, and it does not matter. Using those classes - for example accessing the static properties - is the important aspect here. There's usually no problem is you mutate the [[prototype]] of a fresh (yet unused) object - the warning is more concerned about doing the mutation in the middle of the objects lifetime.