本文介绍一种 JavaScript 继承实现方式,通过将父类静态方法拷贝至子类,并设置子类 prototype.prototype 指向父类 prototype 来构建原型链。该方法避免了直接赋值 prototype 导致的父类方法被覆盖问题,同时通过保留 parent 引用支持在子类中调用父类方法。
作者:尹伟铭
博客地址:http://my.donews.com/yinwm/
JavaScript 类的定义与继承基础
如前文所述,在 JavaScript 中,类本质上是一个函数。类的静态方法作为该函数的属性存在,而实例方法则定义在 prototype 对象上。
function ClassA() {
}
ClassA.staticMethod = function () {
}
ClassA.prototype.instanceMethod = function () {
}
静态方法拷贝与原型链构建
在此实现中,继承过程首先是将父类的所有静态方法拷贝到子类中,从而使子类拥有父类的静态方法。
接着,将子类的 prototype.prototype 指向父类的 prototype。
最后,可根据实际需求重写部分方法。
function ClassB() {
}
ClassB.staticMethod = ClassA.staticMethod;
ClassB.prototype.prototype = ClassA.prototype;
ClassB.prototype.instanceMethod = function () {
// method 2
}
原型链继承与父类方法调用
对于子类,通过 prototype 链来实现实例方法的继承。选择这种实现方式的原因是,子类需要重写某些方法。由于 prototype 是引用类型,如果直接写作 ClassB.prototype = ClassA.prototype,在重写 ClassB 的实例方法时会同时破坏 ClassA 的实例方法。修改后的方法会屏蔽父类方法。
方法查找顺序为:instanceA.prototype.method -> ClassA.prototype。
此时类方法的继承已实现,接下来需要在子类中调用父类方法。
在 Java 中,这种用法非常常见。
public void method() {
super.method();
}
在 JavaScript 中,为了实现此功能,必须保留一个 parent 引用,指向 ParentClass.prototype。
ClassB.prototype.parent = ClassA.prototype;
在 instanceB 中,通过调用 this.parent.method.call(this) 即可使用父类方法。使用 call 是为了将当前对象的数据传递到父类中。更优雅的解决方案尚未提出。
完整代码与 extend 方法实现
因此,完整的代码如下:
function ClassA() {
}
ClassA.prototype.method1 = function () {
}
ClassA.staticMethod = function () {
}
function ClassB(){
}
ClassB.staticMethod = ClassA.staticMethod;
ClassB.prototype.prototype = ClassB.prototype.parent = ClassA.prototype;
将此逻辑抽象为一个 extend 方法:
var LCore = function () {
}
LCore.extend = function (destination, source) {
// copy all functons
for (var prop in source) {
if (prop == “prototype”) {
continue;
}
destination.prototype[prop] = source[prop];
}
// make a reference for parent and reference prototype
destination.prototype.prototype = destination.prototype.parent = source.prototype;
return destination;
}