ES6 CLASS语法糖与ES5的对应关系

1.前言

es6中,引入了class的概念,让我们在用JS实现类的操作的时候更加的顺手,具体使用方法如下:

class a {
    constructor() {
        this.name = 'me';
    }
    sayHello() {
        console.log('hello world');
    }
}
var b = new a();
console.log(b.name); // me
b.sayHello() // hello world

但实际上,class只是把我们es5构建构造函数的prototype等隐藏起来了,由以下操作:

typeof a === 'function'

可以看出,其实class只是我们es5构造函数的一种语法糖。但是这种写法,更符合面向对象编程的习惯。以上例子用es5写出:

function a () {
    this.name = 'me';
}
a.prototype.sayHello = function () {
    console.log('hello world');
}

2.静态方法static

es6中的class提出了静态方法static的概念,静态方法不会挂载到这个类所构建的实例上:

class a {
    constructor() {
        this.name = 'me';
    }
    sayHello() {
        console.log('hello world');
    }
    static sayHi() {
        console.log('hi');
    }
}
var b = new a();
console.log(b.name); // me
b.sayHello() // hello world
b.sayHi() // Uncaught TypeError: b.sayHi is not a function
a.sayHi() // hi

当实例b调用sayHi()时会报错,其es5实现如下:

function a () {
    this.name = 'me';
}
a.prototype.sayHello = function () {
    console.log('hello world');
}
a.prototype.constructor.sayHi = function () {
    console.log('hi');
}

由于构造函数原型prototype的constructor指向其本身,向其中添加方法就不会被其实例访问到了。

3.继承

es6的class同样有一套非常优雅的继承写法,如下:

class parent {
    constructor() {
        this.name = 'parent and son';
        this.id = 'parent';
    }
    sayHello() {
        console.log('hello world');
    }
    static sayHi() {
        console.log('hi');
    }
}
class son extends parent {
    constructor() {
        super();
        this.id = 'son';
        this.subName = 'son and parent';
    }
    showId() {
        console.log(this.id);
    }
}
var t = new son();
t.id; // son
t.subName; // son and parent
t.name; // parent and son
t.sayHello() // hello world
t.showId() // son
t.sayHi() // Uncaught TypeError: t.sayHi is not a function

子类son继承了父类parent的name和id属性,同时也继承了sayHello方法,同时又有自己的属性subName和showId。可以看到在继承的时候,子类son在constructor中需要执行super()才能调用this,那是因为在子类constructor的时候this还没生成,super()的作用是将父类实例化然后再与子类的属性和方法一起加工成子类的this,类似于parent.prototype.constructor.call(this, props),其es5实质上就是一个组合继承:

// 父类
function parent () {
    this.name = 'parent and son';
    this.id = 'parent';
}
parent.prototype.sayHello = function () {
    console.log('hello world')
}
parent.prototype.constructor.sayHi = function() {
    console.log('hi');
}
// 子类
function son () {
    parent.call(this);
    this.id = 'son';
    this.subName = 'son and parent';
}
son.prototype = new parent();
// 因为直接字面量定义了son.prototype
// son.prototype.constructor === parent
// 因此要将其重新指回son
son.prototype.constructor = son;
son.prototype.showId = function () {
    console.log(this.id);
}

4. 通用的编译方案

es5中有个函数叫Object.defineProerty,我们可以用这个属性创建一个公用的方法来编译es6中的class。(来自于class的编译参考)

var _createClass = (function() {
    function defineProperties(target, props) {
        // 遍历要放入的属性和方法
        for (var i = 0; i < props.length; i++) {
            // 拿出每一项
            var descriptor = props[i];
            // 属性或方法是否可以枚举
            descriptor.enumerable = descriptor.enumerable || false;
            // 是否能被delete删除
            descriptor.configurable = true;
            // 如果有值的则可以修改
            if ("value" in descriptor) descriptor.writable = true;
            // 设置对应属性
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }
    // 生成构造函数的核心
    return function(Constructor, protoProps, staticProps) {
        // 如果是要继承的属性则放到prototype中
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        // 如果是静态属性则放在自身
        if (staticProps) defineProperties(Constructor, staticProps);
        return Constructor;
    };
})();
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
// 例子
class test {
    constructor() {
        this.name = 'test'
    }
    sayHello() {
        console.log('hello world');
    }
    static sayHi() {
        console.log('hi')
    }
}
// 用上述方法来生成
var otest = (function () {
    function otest () {
        _classCallCheck(this, otest);

        this.name = 'test';
    }
    _createClass(otest, [{
        key: 'sayHello',
        value: function sayHello () {
            console.log('hello world');
        }
    }], [{
        key: 'sayHi',
        value: function sayHi () {
            console.log('hi')
        }
    }])
    return otest;
})()