本文深入解析 JavaScript 在没有 class 关键字情况下的继承实现机制。通过利用 __proto__ 原型链特性,详细阐述如何编写 extend 函数以实现子类对父类属性和方法的自动继承。文章还涵盖了 instanceof 支持、方法重写(Override)中父类方法的调用技巧,以及构造器的处理,并结合 People、Employee 和 Manager 的三级类结构示例,展示了完整的代码实现与使用方式。
JavaScript 继承的背景与挑战
JavaScript 在设计初期并未纳入面向对象特性。即便在如今现代浏览器普及的时代,各类 JavaScript 框架和库层出不穷,但语言本身依然没有 class 关键字。若要定义类,仍需借助 function,而继承、重载等功能则难以直接实现。
然而,缺乏继承机制会导致代码复用困难。若将所有共有逻辑逐一拷贝,仅实现最基础的代码复用,显然不是高效的做法。
因此,我们需要手动实现继承机制。
目标
核心目标是实现继承,即子类能够自动继承父类的所有公共属性和方法。
必须支持 instanceof 操作符。例如,若 c 是子类的实例,P 是父类,则 c instanceof P 应返回 true。
其次,需支持对父类方法的重写(Override),并允许在子类方法中便捷地调用父类的同名方法。
至于重载(Overload),由于 JavaScript 语言特性限制(不允许存在同名但参数列表不同的方法),无法实现。
继承机制的设计与实现
设计与实现
JavaScript 对象拥有一个重要属性 __proto__,即原型。原型本身也是一个对象,因此它也可以拥有自己的原型,从而形成原型链。当调用对象的方法或读取属性时,JavaScript 引擎的执行逻辑如下:
1. 首先在该对象中查找对应的方法或属性,若未找到; 2. 则在该对象的原型中查找,若仍未找到; 3. 继续在原型的原型中查找; 4. ...; 5. 直到最终找到 Object 的原型为止,若仍未找到则返回 undefined。 如下图所示:

原型链的这一特性与继承概念高度相似,因此利用它来实现继承机制是自然的选择。同时,原型链对 instanceof 的支持使其成为理想方案。
我们定义一个 extend 函数,接收两个参数:第一个为父类,第二个为子类,定义如下:
function extend(ParentClass, ChildClass) {
...
return ChildClass;
}
该函数对子类进行处理并返回子类。处理逻辑如下:
建立原型链
通过将子类的原型链与父类的原型链连接,子类即可自动拥有父类的方法和属性:
var pp = ParentClass.prototype,
cp = ChildClass.prototype;
function T() {};
T.prototype = pp;
ChildClass.prototype = new T();
为了连接原型链,需创建一个父类的实例并赋给子类的原型属性。但为了避免在 extend 方法内部直接实例化父类,此处引入一个中间类 T 来解决该问题。
实现重写
原型链建立后,需保留原子类原型上的方法和属性:
方法
若父类存在同名方法,我们使用闭包保留对父类方法和子类方法的引用。随后,修改新原型中该方法的引用,指向一个新的 function。在此 function 内部,创建一个临时属性 super,指向父类方法,并调用子类方法。这样,在子类方法中可通过 this.super 调用父类方法:
ChildClass.prototype[name] = (function(pm, cm) {
return function() {
var _super = this.super;
this.super = pm;
var result = cm.apply(this, arguments);
this.super = _super;
return result;
};
})(pp[name], cp[name]);
属性
属性不存在重写问题,因此直接将子类原原型中的属性添加到新原型中即可:
ChildClass.prototype[name] = cp[name];
构造器
为使子类能访问父类的构造器,将父类赋给子类的 super 属性:
ChildClass.super = ParentClass;
继承机制的实际应用示例
如何使用
假设设计一个管理系统,涉及客户、工人和经理。将客户和员工的共性抽象为人(People);将工人和经理的共性抽象为员工(Employee)。由此得到三级类结构:

实现该设计的代码如下:
function People(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
function Employee(firstname, lastname, company) {
Employee.super.apply(this, arguments);
this.company = company;
}
function Manager(firstname, lastname, company, title) {
Manager.super.apply(this, arguments);
this.title = title;
}
我们希望为每个人提供描述,People 的格式为姓+名;员工在姓+名后附加公司名称;经理则在员工描述基础上附加职位。代码如下:
People.prototype.summary = function() {
return this.firstname + " " + this.lastname;
};
Employee.prototype.summary = function() {
return this.super.call(this) + ", " + this.company;
};
Manager.prototype.summary = function() {
return this.super.call(this) + ", " + this.title;
};
在所有成员方法定义完毕后,声明类的继承(必须先定义方法,再声明类的继承,否则无法在方法中使用 this.super 调用父类方法):
extend(People, Employee);
extend(Employee, Manager);
使用这些类较为简单,直接 new 即可:
var people = new People("Alice", "Dickens");
var employee = new Employee("Bob", "Ray", "Alibaba");
var manager = new Manager("Calvin", "Klein", "Alibaba", "Senior Manager");
console.log( people.summary() ); //Alice Dickens
console.log( employee.summary() ); //Bob Ray, Alibaba
console.log( manager.summary() ); //Calvin Klein, Alibaba, Senior Manager
这篇文章不错吧,那就给个赞吧!