在 JavaScript 中实现类继承,最简单的方式是通过 es6 中的关键字 class 和 extends. 下面代码是让类 S 继承类 P:
class P {
constructor(a) {
this.a = a;
}
}
class S extends P {
constructor(a, b) {
super(a);
this.b = b;
}
}
new S(1, 2);
ps: 语法层在实现时,要求 S 中必须调用 super([])
。
下面看在不使用 class/extends 关键字情况下,如何实现 extends。 使用 babel(可直接使用在线工具 https://babeljs.io/repl/)转语法,然后分析转换的结果。
// 为了保证可以继承父类的 (非静态) 属性
// 如果父类返回对象,就直接使用此对象作为实例
// 否则,使用当前上下文 (在这里是 self -> this)
// note:
// 在 JavaScript 中,是支持返回对象做实例的
// 建议查看相应资料来了解 JavaScript 中的类定义方式
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return call && (typeof call === "object" || typeof call === "function")
? call
: self;
}
// 继承的主要实现
function _inherits(subClass, superClass) {
// 保证父类只可能是 null 或 function - 其它类型没意义
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError(
"Super expression must either be null or a function, not " +
typeof superClass
);
}
// 把子类原型重写为一个对象
// 并且此对象的 __proto__ 属性指定 superClass.prototype
// 以保证原型链指向
// 比如:
// subclass.__proto__ -> subClass.prototype
// subClass.prototype.__proto__ -> superClass.prototype
// 最终让 subClass 实例可访问到 superClass 原型上的方法/属性
subClass.prototype = Object.create(superClass && superClass.prototype, {
// 维持 class.prototype.constructor === class
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
// 让子类在继承父类属性/方法 - 静态
// 子类当作实例,实例的 __proto__ 属性指向一对象,
// 那么实例可直接访问对象上的属性/方法
if (superClass)
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass);
}
// 用来检查类调用方式
// 仅支持 new className 方式调用
// 不像 jQuery 等,直接内部转换
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var P = function P(a) {
_classCallCheck(this, P);
this.a = a;
};
var S = (function(_P) {
_inherits(S, _P);
function S(a, b) {
_classCallCheck(this, S);
var _this = _possibleConstructorReturn(
this,
// 直接使用:
// _P.call(this, a)
// 有什么区别?
(S.__proto__ || Object.getPrototypeOf(S)).call(this, a)
);
_this.b = b;
return _this;
}
return S;
})(P);
new S(1, 2);