likes
comments
collection
share

class X extends P {} 的 ES5 解释

作者站长头像
站长
· 阅读数 16

class X extends P {} 是 ES6 引入一个新语法,用来实现类继承,也就是子类继承父类。

那是如何实现的呢?

将代码 转译成 ES5 后,得:

function _assertThisInitialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
}
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
        }_createSuper
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
}
function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}
function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}
var _typeof = function(obj) {
    "@swc/helpers - typeof";
    return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
};
function _isNativeReflectConstruct() {
    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
    if (Reflect.construct.sham) return false;
    if (typeof Proxy === "function") return true;
    try {
        Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
        return true;
    } catch (e) {
        return false;
    }
}
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);
    };
}
var X = function(P1) {
    "use strict";
    _inherits(X, P1);
    var _super = _createSuper(X);
    function X() {
        _classCallCheck(this, X);
        return _super.apply(this, arguments);
    }
    return X;
}(P);

代码比较多,不用急,我们来一点点分析其实现。先从核心代码开始。

核心代码

...

var X = function(P1) {
    "use strict";
    _inherits(X, P1);
    var _super = _createSuper(X);
    function X() {
        _classCallCheck(this, X);
        return _super.apply(this, arguments);
    }
    return X;
}(P);

《class X {} 的 ES5 解释》中讲解过“类中代码是在严格模式下执行的,并且只能以 new方式调用”——子类也不例外。

var X = function(P1) {
    "use strict";
    ...
    function X() {
        _classCallCheck(this, X);
       ...
    }
    ...
}(P);

那么内部是如何实现子类继承父类的呢?答案是通过 _inherits(subClass, superClass) 方法。

_inherits(subClass, superClass)

_inherits() 是编译器封装的一个方法,用来实现子类继承父类。

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);
}

var X = function(P1) {
    "use strict";
    _inherits(X, P1);
    ...
}(P);

subClass 表示子类,superClass 则表示要继承的父类。在 class X extends P {} 场景下,X 对应 subClassP(即编译后的 P1)对应 superClass

我们来看下 _inherits() 内部实现。分两步:父类类型检验以及执行子类继承。

一、父类类型检验

如果 typeof superClass !== "function" && superClass !== null 判断成立,就报错 TypeError。就是说父类的值只能是函数或 null

可能不太好理解,我们将判断换成等价的 !(typeof superClass === "function" || superClass === null),就是说当 superClass 值为函数或 null 时,不会报错。

二、执行子类继承

function _inherits(subClass, superClass) {
    ...
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true,
            configurable: true
        }
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
}

有两条继承关系:

  • subClass.prototype(包含不可枚举属性 constructor) 的原型对象指向 superClass.prototype——用于实例属性的继承。
    • 实现机制:通过 Object.create(proto, propertiesObject)方法在创建 subClass.prototype 的同时,将内部原型对象指向 superClass.prototype
  • subClass 的原型对象指向 superClass——用于类静态属性继承。
    • 实现机制:_setPrototypeOf(subClass, superClass) 函数内部调用了 Object.setPrototypeOf(subClass, superClass) / subClass*._*proto__ = superClass 来设置原型

两条继承关系通过下图一目了然。

class X extends P {} 的 ES5 解释

[[[prototype]] 是一个对象内部属性,指向对象原型对象

至此,已经实现了 subClasssuperClass 的继承关系。

还有一个地方,就是在实例化子类的时候,需要调用父类构造函数。

调用父类构造函数

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);
    };
}

function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}

var X = function(P1) {
    ...
    var _super = _createSuper(X);
    function X() {
        ...
        return _super.apply(this, arguments);
    }
    return X;
}(P);

分析下代码执行过程:

  • _createSuper(X) 会返回一个包装函数(即 _super),函数会在子类 X 实例化时执行;
  • new X() 时,执行包装函数 _super,包装函数的内部逻辑是查找并调用当前子类的父类构造函数(P),然后返回执行结果。

当然,这是在子类没有任何逻辑情况下。当子类中包含逻辑时:

class X extends P {
    constructor(a) {
        super(a);
        this.b = 'b';
    }
}

将代码 转译成 ES5 后,核心代码。

var X = function(P1) {
    "use strict";
    _inherits(X, P1);
    var _super = _createSuper(X);
    function X(a) {
        _classCallCheck(this, X);
        var _this;
        // 关键代码
        _this = _super.call(this, a);
        _this.b = "b";
        return _this;
    }
    return X;
}(P);

当子类构造函数中包含逻辑时,会先执行父类构造函数(_this = _super.call(this, a)),再在返回的对象基础上执行子类构造函数里的逻辑(_this.b = "b"),并返回最终的实例对象(_this)。

总结

因此 class X extends P {} 内部执行如下:

  1. X.prototype 的原型对象设置成 P.prototype
  2. X 的原型对象设置成 P
  3. 执行 P 构造函数逻辑
  4. 执行 X 构造函数逻辑