class是ES6的语法糖,如下的class写法,会被babel转为ES5函数实现类的方式:
// es6 class 写法
class Person{
constructor(name, age){
this.name = name
this.age = age
}
sayName (){
return this.name
}
}
class Men extends Person{
constructor(name, age){
super()
this.gender = 'male'
}
}
经过babel转换的代码很长,我这里就不贴了,需要查看的请自行到 babel在线转换网站看一下。
父类的创建方式
首先 class Person 会被转换成这样
var Person = /*#__PURE__*/ (function () {
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [
{
key: "sayName",
value: function sayName() {
return this.name;
}
}
]);
return Person;
})();
其实就是非常熟悉的es5创建类的方法:
function P {
// 判断是不是通过new 调用
if (this instance of P) return Error('.......')
this.x = 1
}
这里的_classCallCheck 就是判断构造函数是不是通过new调用
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
_createClass(class, properties)这个函数将properties以键值对数组的形式将属性挂到Person.prototype上,挂在prototype上的变量/函数在继承时多个子实例公用父实例prototype上的变量,节省开销。具体可以参考我的另外一篇博文 Javascript ES5实现继承的N种方式。
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
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);
}
}
核心就是 Object.defineProperty(target, descriptor.key, descriptor); 至此,第一个类Person创建好了。
子类继承父类
子类Men被Babel转换成如下代码
var Men = /*#__PURE__*/ (function (_Person) {
_inherits(Men, _Person);
var _super = _createSuper(Men);
function Men(name, age) {
var _this;
_classCallCheck(this, Men);
_this = _super.call(this);
_this.gender = "male";
return _this;
}
return Men;
})(Person);
先来看 _inherits这个函数,这个函数完成了子类继承父类prototype,并将Men.prototype.constructor设置为自身构造函数。下面看一下怎么做到的。
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
这里比较核心的是
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
Object.create(proto,[propertiesObject]) 会先创建一个空对象o,o.__proto__指向proto,o对象的属性为后面的属性数组制定的值。详情见 Object.create-MDN 现在Men.prototype被设置为superClass.prototype 也就是Person这个类函数。Men.prototype.constructor被设置为Men.
接着执行 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);
}
使得 Men.__proto__ = Person 完成原型链拼接。
接下来就是
var _super = _createSuper(Men);
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
这里返回了一个函数fn,并通过闭包保存在Super变量。它的大致的逻辑如下:
-
var Super = _getPrototypeOf(Derived), 从Men 这个类函数的prototype取出Person的构造函数。
-
var NewTarget = _getPrototypeOf(this).constructor; 获取Men构造函数Men;
-
result = Reflect.construct(Super, arguments, NewTarget); Reflect.construct相当于执行 new 操作符, 前两个参数是new 的构造函数和参数,相当于执行了new Person(arguments),第三个参数NewTarget是让这个新创建的对象成为NewTarget的实例(Men实例)。关于 Reflect.construct的参考Reflect.construct参考-stack-overflow 这里result 为:
Men() { this.name: undefined, this.age: undefined }
-
return _possibleConstructorReturn(this, result); 检测result类型,没有问题直接返回result。
这个Super的作用是在后续通过new Men创建子类实例时,通过 _this = _super.call(this);将父类通过this.xxx = xxx设置的属性挂到子类Men上。最后把Men的构造函数返回,其余的部分和Person类并无二致。