JavaScript 三种继承模式详解:原型链、类式与复制继承

作者:木棉时间:2026-10-27 09:01:22浏览:453

本文深入解析 JavaScript 中的三种主要继承模式:原型链继承、类式继承以及复制继承。通过代码示例详细阐述原型链如何通过临时构造函数实现对象属性与方法继承;类式继承如何利用中间构造函数隔离原型以避免父类引用被修改;以及复制继承如何处理对象与数组类型的深拷贝问题。文章对比了各模式的内存占用、实现复杂度及适用场景,帮助开发者根据实际需求选择最佳继承方案。

所谓继承,简单来说就是直接获取他人已有的成果。

采用继承的核心目的在于复用现有资源,避免重复造轮子。

既然复用资源是普遍需求,掌握具体的继承实现方法便显得尤为重要。

在深入具体实现之前,需要预先理解构造函数、对象模型以及原型链等基础概念。

JavaScript 继承方式概览

JavaScript 中常见的继承实现方式主要有两种:原型链继承与类式继承。

    原型链继承适用于对象间的继承,类式继承适用于构造函数间的继承。

原型链继承实现

首先是原型链继承方式。

具体实现代码如下:

// 定义需要被继承的父对象
var parent = {
name: "baba"
, say: function () {
alert("I am baba");
}
}
// 创建新对象
var child = proInherit(parent);
// 验证测试结果
alert(child.name); // 输出 "baba"
child.say(); // 输出 "I am baba"

通过调用 proInherit(obj) 函数并传入目标对象,即可实现属性与方法的继承。该函数并非 JavaScript 内置方法,需手动定义,其实现逻辑非常简洁:

具体代码如下:

function proInherit(obj) {
function F() {}
F.prototype = obj;
return new F();
}

这里定义了一个临时的空构造函数 F(),将其 prototype 指向父对象。同时,F 的实例通过 链接继承了父对象的所有功能。

其原型链结构如下图所示:

         JavaScript 继承方式概览

 

类式继承实现

其次是类式继承方式。

具体实现代码如下:

// 定义父类构造函数
function Parent() {
this.name = "baba";
}
// 定义父类原型方法
Parent.prototype.getName = function () {
return this.name;
}
// 定义子类构造函数
function Child() {
this.name = "cc";
}
// 执行类式继承
classInherit(Parent, Child);
// 创建实例
var child = new Child();
alert(child.getName()); // 输出 "baba"

接下来分析实现继承的关键方法 classInherit(Parent, Child)。

具体代码如下:

var classInherit = (function () {
var F = function () {}
return function (P, C) {
F.prototype = P.prototype;
C.prototype = new F();
C.prototype.constructor = C;
}
}());

对该方法的分析如下:

    首先创建一个空的构造函数 F(),利用其实例的 属性来构建父类与子类的原型链,起到代理作用。此举旨在防止直接执行 C.prototype = P.prototype,否则在修改子类实例属性或方法时会连带影响父类。整体采用立即执行函数并在闭包中存储 F(),可避免多次继承时创建大量空构造函数,从而节省内存。最后一行代码的作用是修正 constructor 指向,因为受原型链影响,C 的实例对象的 constructor 默认会指向 P,因此需要重新设置为 C。

其原型链结构如下图所示:

            JavaScript 继承方式概览

虽然这种方式在实例化时能够继承原型方法,但无法直接继承父类的实例属性。为此,下文介绍一种复制继承方式,作为对类式继承的补充。

复制继承实现

复制继承的实现方式如下。

具体代码如下:

// 复制继承函数定义
function copyInherit(p, c) {
var i,
toStr = Object.prototype.toString,
astr = "[object Array]";
c = c || {};
for (i in p) {
if (p.hasOwnProperty(i)) {
if (typeof p[i] === "object") {
c[i] = toStr.call(p[i]) == astr ? [] : {};
c[i] = copy(p[i], c[i]);
}
} else {
c[i] = p[i];
}
}
}
return c;
}
// 重新定义 Parent 类
function Parent() {
this.name = "pp";
this.obj = { a: 1, b: 2 };
this.arr = [1, 2];
}
// 创建实例
var child = new Child();
var parent = new Parent();
copyInherit(parent, child);
alert(child.name); // 输出 "baba"
alert(child.arr); // 输出 1,2
alert(child.obj.a); // 输出 1

对 copyInherit(p, c) 函数的分析如下:

在赋值操作中,存在传值与传引用两种机制。当父对象属性包含数组或对象类型时,语句 c[i] = toStr.call(p[i]) == astr ? [] : {}; 能够确保子对象属性被正确初始化,从而避免修改子对象属性时意外篡改父对象属性。

继承模式总结与对比

总结三种继承模式的特点:

类式继承因符合大众对构造函数的认知而较为普遍,但其内存占用相对较高。原型式继承(即原型链继承)内存占用较小,但在处理数组或对象类型的克隆时较为繁琐。复制继承实现简单且应用广泛。

 

作者最新文章