JavaScript 面向对象封装与继承的多种实现方式

作者:拾光

时间:2026-10-28

浏览:473

本文系统梳理了JavaScript中面向对象的封装与继承技术。封装部分涵盖原始模式、构造函数模式及Prototype模式,对比了实例成员与静态成员的创建方式。继承部分深入解析了构造函数绑定(apply/call)、Prototype原型链继承以及拷贝继承三种主流方案,并分析了各方案的优缺点与适用场景,帮助开发者根据实际需求选择最合适的实现策略。

文章目录

JavaScript 面向对象封装与继承概述

本文旨在梳理JavaScript面向对象编程中核心的封装与继承机制。

在JavaScript中,实现对象封装的方式多样,本文将重点介绍几种常用且典型的实现模式。

JavaScript 对象封装的三种模式

1.1 原始模式生成对象 直接将我们的成员写入对象中,用函数返回。 缺点:很难看出是一个模式出来的实例。

代码示例:

代码如下:

function Stu(name, score) {
return {
name: name,
score: score
            }
        }
var stu1 = Stu("张三", 80);
var stu2 = Stu("李四", 90);
console.log(stu1.name); // 张三

1.2 生成构造模式对象

JavaScript提供了一种通过构造函数创建对象的标准模式。所谓“构造函数”,本质上是一个普通函数,但其内部使用了 this 变量来引用即将创建的对象实例。当使用 new 关键字调用该构造函数时,this 会自动绑定到新创建的实例对象上。

具体代码实现如下:

代码如下:

function Stu(name, score) {
this.name = name,
this.score = score
        }
var stu1 = new Stu("张三", 80);
var stu2 = new Stu("李四", 90);
console.log(stu1.name + "/" + stu2.score); // 张三 90
console.log((stu1.constructor == Stu) + "/" + (stu2.constructor == Stu)); // true true
console.log((stu1 instanceof Stu) + "/" + (stu2 instanceof Stu)); // true true

可以看出,JavaScript中通过构造函数生成对象的方式与C#中使用 class 定义对象类似,均是通过模板定义成员,并利用 new 关键字进行实例化。

为了对比,以下是使用C#代码生成相同 Stu 对象的示例:

代码如下:

Class Stu
{
public string name;
public double score;
}

至此,基本的对象结构已建立。接下来,我们需要为所有对象提供一个公用的方法,且该方法只需创建一次,不随每个实例的 new 操作而重复生成。在C#中这通常通过静态成员实现,那么在JavaScript中该如何处理呢?

1.3 Prototype模式

在JavaScript中,每个构造函数都拥有一个 prototype 属性,该属性指向的对象所包含的所有属性和方法,都会被构造函数的实例所继承。因此,直接向 prototype 添加成员,其效果类似于在C#中声明静态成员。

代码示例:

代码如下:

function Stu(name, score) {
this.name = name,
this.score = score
        }
Stu.prototype.type='学生';
Stu.prototype.log = function (s) {
console.log(s);
        }
var stu1 = new Stu("张三", 80);
var stu2 = new Stu("李四", 90);
console.log(stu1.type + "/" + stu2.type); // 学生 学生
stu1.log('hello'); // hello
console.log(stu1.log == stu2.log); // true

JavaScript 继承的多种实现方案

关于封装的讨论暂告一段落,接下来我们将探讨JavaScript中继承的实现机制。

2. 继承

2.1 构造函数绑定

通过在子函数内部直接调用 call 或 apply 方法,将父对象的构造函数绑定到子对象上,从而实现继承。

代码如下:

function Stu(name, score) {
Grade.apply(this, arguments);
//Grade.call(this, arguments);
this.name = name,
this.score = score
        }
function Grade() {
this.code = "初中";
this.ask = function () {
console.log("大家好");
            }
        }
var stu1 = new Stu("张三", 80);
var stu2 = new Stu("李四", 90);
console.log(stu1.code); // 初中
stu1.ask(); // 大家好

这里的 apply 方法执行了两个关键操作:首先将第一个参数 this 传递给 Grade 构造函数(作为调用者),然后执行 Grade 内部的代码。这相当于在 Stu 中重新执行了一遍 Grade 中使用 this 定义的成员,从而实现了属性的继承。

2.2 通过 prototype 继承 首先查看相关代码实现。

代码示例:

代码如下:

function Stu(name, score) {
this.name = name,
this.score = score
        }
function Grade() {
this.code = "初中";
        }
Stu.prototype = new Grade();
Stu.prototype.constructor = Stu; //防止继承链的紊乱,手动重置声明
var stu1 = new Stu("张三", 80);
var stu2 = new Stu("李四", 90);
console.log(Stu.prototype.constructor); // 自己的构造函数
console.log(stu1.code); // 初中

前文提到,prototype 类似于C#中的静态成员,因此我们可以将父类的所有成员通过 prototype 转化为子类的“静态”成员来实现继承。

然而,通过 prototype 继承存在一个缺点:所有继承来的成员都表现为静态特性。那么,如何正确继承对象实例成员呢?

2.3 拷贝继承

拷贝继承的核心思想是将父对象的所有属性和方法逐一拷贝到子对象中,以此实现继承效果。

代码示例:

代码如下:

function Stu(name, score) {
this.name = name,
this.score = score
        }
function Grade() {}
Grade.prototype.code = "初中";
    }
//函数封装
function extend(C, P) {
var p = P.prototype;
var c = C.prototype;
for (var i in p) {
c[i] = p[i];
            }
        }
extend(Stu, Grade);
var stu1 = new Stu("张三", 80);
var stu2 = new Stu("李四", 90);
stu1.code='高中';
console.log(stu1.code); // 高中
console.log(stu2.code); // 初中
console.log(Stu.prototype.constructor);
console.log(Grade.prototype.constructor)

至此,JavaScript面向对象的主要封装与继承方式已梳理完毕。需要注意的是,这些模式并非一成不变,实际开发中应根据具体需求进行灵活调整。正如所言,合适的方案才是最好的。

本文仅针对封装和继承进行了深入分析,后续还将推出其他相关文章,帮助读者更深入地理解JavaScript面向对象编程。以上均为个人学习心得,如有疏漏,欢迎联系指正。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章