0%

Babel如何编译class

ES6的class只是ES5的构造函数的语法糖,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

1.Babel编译Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Point {
age = 100;
static gender = 'male';
constructor(x, y) {
this.x = x;
this.y = y;
}

toString() {
return '(' + this.x + ', ' + this.y + ')';
}

static sayStatic() {
return 'static'
}

get name() {
return 'zj';
}

set name(newName) {
console.log('newName为:' + newName);
}
}
  • constructor对应ES5的构造函数

  • class里所有的方法都是在构造函数的prototype上
    在ES6中类的内部所有定义的方法,都是不可枚举的(non-enumerable),而ES5中除了constructor都是可枚举的

  • static是静态类型,就表示该方法不会被实例继承,而是直接通过类来调用

    当然我们定义实例属性,不必非要在constructor中定义,可以在外部定义,加static表示的静态属性,不加的则是实例属性,而且babel已经支持

  • class必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

我们可以在Babel官网的 Try it out 页面查看ES6的代码编译成什么样子?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
"use strict";

//instanceof本质是调用Symbol.hasInstance方法,判断对象是否是构造器的实例
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}

//_classCallCheck的作用是检查 Point 是否是通过 new 的方式调用,类必须使用 new 调用,否则会报错。

//当我们使用 let point = Point() 的形式调用的时候,this 指向 window,所以 instance instanceof Constructor 就会为 false,与 ES6 的要求一致。
function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}

//使用 Object.defineProperty 方法添加属性。默认 enumerable 为 false, configurable 为 true,这也就解释了上面所说的,ES6类中定义的方法都是不可枚举类型的。然后通过判断 value 是否存在, 来判断是否是 getter 和 setter。 如果存在 value, 就为 descriptor 添加 value 和 writable 属性, 如果不存在, 就直接使用 get 和 set 属性。
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);
}
}
//Babel 生成了一个 _createClass 辅助函数,该函数传入三个参数,第一个是构造函数,在这个例子中也就是 Point,第二个是要添加到原型上的函数数组,第三个是要添加到构造函数本身的函数数组,也就是所有添加 static 关键字的函数。该函数的作用就是将函数数组中的方法添加到构造函数或者构造函数的原型中,最后返回这个构造函数。在其中,又生成了一个 defineProperties 辅助函数。
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}

function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}

var Point = /*#__PURE__*/ function () {
function Point(x, y) {
_classCallCheck(this, Point);

_defineProperty(this, "age", 100);

this.x = x;
this.y = y;
}

_createClass(Point, [{
key: "toString",
value: function toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}, {
key: "name",
get: function get() {
return 'zj';
},
set: function set(newName) {
console.log('newName为:' + newName);
}
}], [{
key: "sayStatic",
value: function sayStatic() {
return 'static';
}
}]);

return Point;
}();

_defineProperty(Point, "gender", 'male');

2.Babel编译继承

ES5的寄生组合式继承

1
2
3
4
5
6
7
8
9
10
11
12
13
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayName = function () {
console.log(this.name);
};

function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);

1

ES6 extend

Class 通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Parent {
constructor(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
sayName() {
console.log(this.name);
}
}

class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
  • super 关键字表示父类的构造函数,相当于 ES5 的 Parent.call(this)
  • 子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。也正是因为这个原因,在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字,否则会报错。

ES6中存在两条继承链:

  • 子类的 __proto__ 属性,表示构造函数的继承,总是指向父类
  • 子类 prototype 属性的 __proto__ 属性,表示方法的继承,总是指向父类的 prototype 属性

相比寄生组合式继承,ES6 的 class 多了一个 Object.setPrototypeOf(Child, Parent) 的步骤。

2

2

extends继承目标

  1. 子类继承Object类

    1
    2
    3
    4
    class A extends Object {
    }
    A.__proto__ === Object // true
    A.prototype.__proto__ === Object.prototype // true
  2. 子类继承null

    1
    2
    3
    4
    class A extends null {
    }
    A.__proto__ === Function.prototype // true
    A.prototype.__proto__ === undefined // true
  3. 不存在任何继承

    1
    2
    3
    4
    class A {
    }
    A.__proto__ === Function.prototype // true
    A.prototype.__proto__ === Object.prototype // true

我们可以在Babel官网的 Try it out 页面查看ES6的代码编译成什么样子?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}

//
function _inherits(subClass, superClass) {
// extend 的继承目标必须是函数或者 null
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
// 类似于 ES5 的寄生组合式继承,使用 Object.create,设置子类 prototype 属性的 __proto__ 属性指向 父类的 prototype 属性,后面添加的constructor意思是在subClass.prototype上添加constructor属性,value是subClass,修正因为修改原型链导致的constructor缺失。
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
// 设置子类的 __proto__ 属性指向父类
if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}

function _createSuper(Derived) {
return function () {
var Super = _getPrototypeOf(Derived),
result;
if (_isNativeReflectConstruct()) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
//对于 Parent.call(this) 的值,如果是 object 类型或者是 function 类型,就返回 Parent.call(this),如果是 null 或者基本类型的值或者是 undefined,都会返回 self 也就是子类的 this
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}

function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}

function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}

function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}

function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a 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);
}
}

function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}

var Parent = /*#__PURE__*/ function () {
function Parent(name) {
_classCallCheck(this, Parent);

this.name = name;
this.colors = ["red", "blue", "green"];
}

_createClass(Parent, [{
key: "sayName",
value: function sayName() {
console.log(this.name);
}
}]);

return Parent;
}();

var Child = /*#__PURE__*/ function (_Parent) {
//建立 Child 和 Parent 的原型链关系,即 Object.setPrototypeOf(Child.prototype, Parent.prototype) 和 Object.setPrototypeOf(Child, Parent)
_inherits(Child, _Parent);

var _super = _createSuper(Child);

function Child(name, age) {
var _this;

_classCallCheck(this, Child);
//调用 Parent.call(this, name),根据 Parent 构造函数的返回值类型确定子类构造函数 this 的初始值 _this。最终,根据子类构造函数,修改 _this 的值,然后返回该值。
_this = _super.call(this, name);
_this.age = age;
return _this;
}

return Child;
}(Parent);

super

  1. 作为函数使用
    • 子类的构造函数必须执行一次super函数。
    • 子类的构造函数中的super()代表的是子类调用父类的构造函数执行,这时候super()中的this,即 父类constructor中的this,指向的是子类
    • super()只能用在子类的构造函数之中,用在其他地方就会报错
  2. 作为对象使用
    • 普通方法中
      • 子类中的super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的
      • 在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例
      • 通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性
    • 静态方法中
      • 用在静态方法之中,这时super将指向父类,而不是父类的原型对象
      • 在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例
-------------本文结束感谢您的阅读-------------