How does Babel.js create compile a class declarati

2019-02-09 17:13发布

My current mission is to convert a JavaScript component ES5 to ES6 (compiled with Babel.js). Before using classes and Babel.js we prototyped to get functions from other components.

com.company.js.ComponentA.prototype = new com.company.js.utils.UltraFunctions()

Now when using Babel.js and turning ComponentA into a class

class ComponentA {
  contructor(){
    this.property = "Proppy";
  }
  doStuff() {
    console.log("doStuff");
  }
}

What happens now when I analyse this Component after instantiating it is that I see two level of prototypes now. The first prototype holds "property" - the second one, which is nested into the first one holds all function in this case "doStuff". That brings up problems with legacy components which should not be converted to classes (yet). Because these components are being put in through the second level prototype they override the prototype which holds the functions of the "synthetic" class compiled by Babel.js.

I am not asking for a solution. I just want to get sure if I am right with the assumption Babel.js converts the classes to ES5 JavaScript. Especially the fact of creating two level prototypes as mentioned above.

Update

I'm sorry I miunderstood the first prototype! as @T.J.Crowder said in the comments the first is the instance - therefore "property" is being smashed into the instance while the functions are being inserted through prototyping to the "first" level prototype. So, replace everything what I said with second level to first level and first level to instance.

2条回答
\"骚年 ilove
2楼-- · 2019-02-09 17:42

I just want to discuss how Babel.js converts a class to ES5 Javascript.

Babel uses a lot of helper functions, or I'd say "just look at the transpiled result." :-)

With ES2015, it's a really simple mapping, because the class syntax was deliberately kept really basic for this first version (ES2016 was going to extend it, but the proposals didn't quite make it so they'll be later, probably ES2017).

class allows us to define:

  • The constructor function (via class and constructor)
  • The constructor function's prototype object's prototype (via extends)
  • Methods to put on the constructor function's prototype object
  • Methods to put on the constructor function itself (static)
  • A means of referencing the base "class" constructor and its prototype information concisely and portably

So this:

// Base "class":
class Base {
    // The code for `Base` goes in this special `constructor` pseudo-method:
    constructor() {
        this.baseProp = 42;
    }

    // A method to put on the `prototype` object (an "instance method"):
    baseMethod() {
        console.log(this.baseProp);
    }

    // A method to put on the constructor (a "static method"):
    static foo() {
        console.log("This is foo");
    }
}

// Derived "class":
class Derived extends Base {
//            ^------------------ defines the prototype behind `Derived.prototype`
    // The code for `Derived`:
    constructor() {
        // Call super constructor (`Base`) to initialize `Base`'s stuff:
        super();

        // Properties to initialize when called:
        this.derivedProp = "the answer";
    }

    // Overridden instance method:
    baseMethod() {
        // Supercall to `baseMethod`:
        super.baseMethod();

        // ...
        console.log("new stuff");
    }

    // Another instance method:
    derivedMethod() {
        this.baseMethod();
        console.log(this.derivedProp);
    }
}

becomes what we might write in ES5 (if we didn't use any helper functions) like this:

// This combines the name defined by `class` with the code defined in `constructor`:
var Base = function() {
    this.baseProp = 42;
};
// The "instance" method:
Base.prototype.baseMethod = function() {
    console.log(this.baseProp);
};
// The "static" method:
Base.foo = function() {
    console.log("This is foo");
};

// The derived constructor
var Derived = function() {
    // Call super constructor (`Base`) to initialize `Base`'s stuff:
    Base.call(this);

    // Properties to add when called:
    this.derivedProp = "the answer";
};

// This was done by `class` and `extends`:
Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;

// Overridden instance method:
Derived.prototype.baseMethod = function() {
    // Supercall to `baseMethod`:
    Base.prototype.baseMethod.call(this);

    // ...
    console.log(this.derivedProp);
};

// Another instance method:
Derived.prototype.derivedMethod = function() {
    this.baseMethod();
    console.log(this.derivedProp);
};

Items of note above:

  • constructor becomes the constructor function
  • All non-constructor, non-static methods become prototype methods
  • static methods are assigned to properties on the constructor function
  • Properties are just properties as usual
  • Creating the object to put on the prototype property of a derived constructor function is done via Object.create(Base.prototype), not new Base().
  • constructor calls the base constructor as its first action.
  • Calls to the super's methods in the ES5 version (Base.prototype.baseMethod.call(this);) are cumbersome and error-prone, one of the great things about the new syntax
查看更多
放我归山
3楼-- · 2019-02-09 17:44

The ES6/Babel class syntax replicates the same prototypal pattern that you would get if you were using function constructors and overwriting the prototype. The examples below will result in the same instance object once calling the constructor.

ES6/Babel

class ComponentA {
      constructor(){
        this.property = "Proppy";
      }
      doStuff() {
        console.log("doStuff");
      }
    }

var c = new ComponentA();

ES5

var ComponentA = function () {
  this.property = "Proppy";
}

ComponentA.prototype.doStuff = function () {
  console.log("doStuff");
}

var c = new ComponentA();

Below is an example of how to inherit the prototype of another constructor in both ES6 class syntax and in ES5.

ES6/Babel

class Parent {
  constructor(){
    this.property = "Proppy";
  }
  doStuff() {
    console.log("doStuff");
  }
}

class ComponentA extends Parent {
  constructor() {
    super();
  }
}

var c = new ComponentA();

ES5

var Parent = function () {
    this.property = "Proppy";
}

Parent.prototype.doStuff = function () {
    console.log("doStuff");
}

function ComponentA () {
  Parent.call(this);
}

ComponentA.prototype = Object.create(Parent.prototype);
ComponentA.prototype.constructor = ComponentA;

var c = new ComponentA();

In the example you've given, your ComponentA is not inheriting the prototype of UltraFunctions() in the same way because you are not resetting the ComponentA.prototype.constructor back to the ComponentA function after overwriting it's prototype.

The result is a ComponentA instance object which in not really an instance of ComponentA but instead it's an instance of UltraFunctions() that was mutated by the ComponentA constructor.

You may have some negative results since a number of Object.prototype methods like .hasOwnProptery are not going to work the way they used to once you migrate your components to use the class syntax.

The other issue with your example is the "constructor" is misspelled. Which will cause the result in Babel to be different than what you may be expecting.

查看更多
登录 后发表回答