preface

Before we look at how Babel compiles classes, let’s look at how ES6 classes correspond to ES5 constructors. After all, ES6 classes are a syntactic candy that does most of what ES5 does. The new class notation just makes writing object prototypes much cleaner and more like object-oriented programming syntax.

constructor

ES6 in:

class Person {
    constructor(name) {
        this.name = name;
    }

    sayHello() {
        return 'hello, I am ' + this.name; }}var kevin = new Person('Kevin');
kevin.sayHello(); // hello, I am Kevin
Copy the code

Corresponding to ES5 is:

function Person(name) {
    this.name = name;
}

Person.prototype.sayHello = function () {
    return 'hello, I am ' + this.name;
};

var kevin = new Person('Kevin');
kevin.sayHello(); // hello, I am Kevin
Copy the code

We can see the ES5 constructor Person, which corresponds to the ES6 Person class’s constructor method.

Note that all methods defined inside a class are non-enumerable.

Take the above example, in ES6:

Object.keys(Person.prototype); / / []
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]
Copy the code

However, in ES5:

Object.keys(Person.prototype); // ['sayHello']
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]
Copy the code

Instance attributes

Previously, we defined instance attributes that could only be written in the constructor method of the class. Such as:

class Person {
    constructor() {
        this.state = {
            count: 0}; }}Copy the code

However, there is a proposal for a new way of writing both instance and static properties that Babel already supports. Now we can write:

class Person {
    state = {
        count: 0
    };
}
Copy the code

Corresponding to ES5:

function Person() {
    this.state = {
        count: 0
    };
}
Copy the code

A static method

All methods defined in a class are inherited by the instance. If you prefix a method with the static keyword, it means that the method is not inherited by the instance, but is called directly from the class. This is called a “static method”.

ES6 in:

class Person {
    static sayHello() {
        return 'hello';
    }
}

Person.sayHello() // 'hello'

var kevin = new Person();
kevin.sayHello(); // TypeError: kevin.sayHello is not a function
Copy the code

Corresponding ES5:

function Person() {}

Person.sayHello = function() {
    return 'hello';
};

Person.sayHello(); // 'hello'

var kevin = new Person();
kevin.sayHello(); // TypeError: kevin.sayHello is not a function
Copy the code

Static attributes

Static properties refer to properties of the Class itself, class.propName, not properties defined on the instance object (this). Previously, we could only add static attributes like this:

class Person {}

Person.name = 'kevin';
Copy the code

Because of the proposal mentioned above, it can now be written as:

class Person {
  static name = 'kevin';
}
Copy the code

Corresponding to ES5:

function Person() {};

Person.name = 'kevin';
Copy the code

The new call

It is worth noting that the class must be called with new or an error will be reported. This is a major difference from normal constructors, which can be executed without new.

class Person {}

Person(); // TypeError: Class constructor Foo cannot be invoked without 'new'
Copy the code

Getter and setter

As in ES5, you can use the get and set keywords inside a “class” to set the store and value functions for an attribute and intercept the access behavior of that attribute.

class Person {
    get name() {
        return 'kevin';
    }
    set name(newName) {
        console.log('new name is: ' + newName)
    }
}

let person = new Person();

person.name = 'daisy';
// The new name is Daisy

console.log(person.name);
// kevin
Copy the code

Corresponding to ES5:

function Person(name) {}

Person.prototype = {
    get name() {
        return 'kevin';
    },
    set name(newName) {
        console.log('new name is: ' + newName)
    }
}

let person = new Person();

person.name = 'daisy';
// The new name is Daisy

console.log(person.name);
// kevin
Copy the code

Babel compilation

So far, we have seen how ES6 and ES5 correspond in the “class” method. In fact, Babel does not convert directly to this form at compile time. Babel generates some helper functions to help implement ES6 features.

You can see what ES6 code looks like compiled on the Try It Out page on Babel’s website.

Compilation (1)

The ES6 code is:

class Person {
    constructor(name) {
        this.name = name; }}Copy the code

Babel compiles to:

"use strict";

function _classCallCheck(instance, Constructor) {
    if(! (instanceinstanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function"); }}var Person = function Person(name) {
    _classCallCheck(this, Person);

    this.name = name;
};
Copy the code

The _classCallCheck checks to see if Person is called with new. As we said above, the class must use new or it will get an error.

When called with var person = person (), this points to the window, so instance instanceof Constructor will be false, as ES6 requires.

Compilation (2)

The ES6 code is:

class Person {
    // Instance properties
    foo = 'foo';
    // Static attributes
    static bar = 'bar';

    constructor(name) {
        this.name = name; }}Copy the code

Babel compiles to:

'use strict';

function _classCallCheck(instance, Constructor) {
    if(! (instanceinstanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function"); }}var Person = function Person(name) {
    _classCallCheck(this, Person);

    this.foo = 'foo';

    this.name = name;
};

Person.bar = 'bar';
Copy the code

Compilation (3)

The ES6 code is:

class Person {
    constructor(name) {
        this.name = name;
    }

    sayHello() {
        return 'hello, I am ' + this.name;
    }

    static onlySayHello() {
        return 'hello'
    }

    get name() {
        return 'kevin';
    }

    set name(newName) {
        console.log('new name is: ' + newName)
    }
}
Copy the code

The corresponding code for ES5 should be:

function Person(name) {
    this.name = name;
}

Person.prototype =  {
    sayHello: function () {
        return 'hello, I am ' + this.name;
    },
    get name() {
        return 'kevin';
    },
    set name(newName) {
        console.log('new name is: ' + newName)
    }
}

Person.onlySayHello = function () {
    return 'hello'
};
Copy the code

Babel compiled as:

'use strict';

var _createClass = function() {
    function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
            if ("value" in descriptor) descriptor.writable = true;
            Object.defineProperty(target, descriptor.key, descriptor); }}return function(Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
        returnConstructor; }; } ();function _classCallCheck(instance, Constructor) {
    if(! (instanceinstanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function"); }}var Person = function() {
    function Person(name) {
        _classCallCheck(this, Person);

        this.name = name;
    }

    _createClass(Person, [{
        key: 'sayHello'.value: function sayHello() {
            return 'hello, I am ' + this.name; }}, {key: 'name'.get: function get() {
            return 'kevin';
        },
        set: function set(newName) {
            console.log('new name is: '+ newName); }}], [{key: 'onlySayHello'.value: function onlySayHello() {
            return 'hello'; }}]);returnPerson; } ();Copy the code

We can see that Babel generates a _createClass helper function that takes in three arguments, the first being the constructor, in this case Person, the second an array of functions to add to the prototype, and the third an array of functions to add to the constructor itself, That is, all functions that add the static keyword. This function adds a method from the array of functions to a constructor or its prototype, and returns the constructor.

In it, a defineProperties helper function is generated to add attributes using the Object.defineProperty method.

The default enumerable is false, and the control system is true, which disables any and all methods like object.keys (). And then you can determine if it’s a getter and setter by checking if the value exists. Add value and writable properties to descriptor if value is present, or use get and set properties if not.

Write in the back

So far, we have seen how Babel compiles a Class. However, one important feature of Class is inheritance. How to inherit a Class and how to compile Babel.

ES6 series

ES6 directory address: github.com/mqyqingfeng…

ES6 series is expected to write about 20 chapters, aiming to deepen the understanding of ES6 knowledge points, focusing on the block-level scope, tag template, arrow function, Symbol, Set, Map and Promise simulation implementation, module loading scheme, asynchronous processing and other contents.

If there is any mistake or not precise place, please be sure to give correction, thank you very much. If you like or are inspired by it, welcome star and encourage the author.