前言
在了解 Babel 是如何编译 class 前,我们先看看 ES6 的 class 和 ES5 的构造函数是如何对应的。毕竟,ES6 的 class 可以看作一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
constructor
ES6 中:
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
复制代码
对应到 ES5 中就是:
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
复制代码
我们可以看到 ES5 的构造函数 Person,对应 ES6 的 Person 类的 constructor 方法。
值得注意的是: 类的内部所有定义的方法,都是不可枚举的(non-enumerable)
以上面的例子为例,在 ES6 中:
Object.keys(Person.prototype); // []
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]
复制代码
然而在 ES5 中:
Object.keys(Person.prototype); // ['sayHello']
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]
复制代码
实例属性
以前,我们定义实例属性,只能写在类的 constructor 方法里面。比如:
class Person {
constructor() {
this.state = {
count: 0
};
}
}
复制代码
然而现在有一个提案,对实例属性和静态属性都规定了新的写法,而且 Babel 已经支持。现在我们可以写成:
class Person {
state = {
count: 0
};
}
复制代码
对应到 ES5 都是:
function Person() {
this.state = {
count: 0
};
}
复制代码
静态方法
所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
ES6 中:
class Person {
static sayHello() {
return 'hello';
}
}
Person.sayHello() // 'hello'
var kevin = new Person();
kevin.sayHello(); // TypeError: kevin.sayHello is not a function
复制代码
对应 ES5:
function Person() {}
Person.sayHello = function() {
return 'hello';
};
Person.sayHello(); // 'hello'
var kevin = new Person();
kevin.sayHello(); // TypeError: kevin.sayHello is not a function
复制代码
静态属性
静态属性指的是 Class 本身的属性,即 Class.propName,而不是定义在实例对象(this)上的属性。以前,我们添加静态属性只可以这样:
class Person {}
Person.name = 'kevin';
复制代码
因为上面提到的提案,现在可以写成:
class Person {
static name = 'kevin';
}
复制代码
对应到 ES5 都是:
function Person() {};
Person.name = 'kevin';
复制代码
new 调用
值得注意的是:类必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。
class Person {}
Person(); // TypeError: Class constructor Foo cannot be invoked without 'new'
复制代码
getter 和 setter
与 ES5 一样,在“类”的内部可以使用 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class Person {
get name() {
return 'kevin';
}
set name(newName) {
console.log('new name 为:' + newName)
}
}
let person = new Person();
person.name = 'daisy';
// new name 为:daisy
console.log(person.name);
// kevin
复制代码
对应到 ES5 中:
function Person(name) {}
Person.prototype = {
get name() {
return 'kevin';
},
set name(newName) {
console.log('new name 为:' + newName)
}
}
let person = new Person();
person.name = 'daisy';
// new name 为:daisy
console.log(person.name);
// kevin
复制代码
Babel 编译
至此,我们已经知道了有关“类”的方法中,ES6 与 ES5 是如何对应的,实际上 Babel 在编译时并不会直接就转成这种形式,Babel 会自己生成一些辅助函数,帮助实现 ES6 的特性。
我们可以在 Babel 官网的 Try it out 页面查看 ES6 的代码编译成什么样子。
编译(一)
ES6 代码为:
class Person {
constructor(name) {
this.name = name;
}
}
复制代码
Babel 编译为:
"use strict";
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Person = function Person(name) {
_classCallCheck(this, Person);
this.name = name;
};
复制代码
_classCallCheck 的作用是检查 Person 是否是通过 new 的方式调用,在上面,我们也说过,类必须使用 new 调用,否则会报错。
当我们使用
var person = Person()
的形式调用的时候,this 指向 window,所以
instance instanceof Constructor
就会为 false,与 ES6 的要求一致。
编译(二)
ES6 代码为:
class Person {
// 实例属性
foo = 'foo';
// 静态属性
static bar = 'bar';
constructor(name) {
this.name = name;
}
}
复制代码
Babel 编译为:
'use strict';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof 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';
复制代码
编译(三)
ES6 代码为:
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 为:' + newName)
}
}
复制代码
对应到 ES5 的代码应该是:
function Person(name) {
this.name = name;
}
Person.prototype = {
sayHello: function () {
return 'hello, I am ' + this