JavaScript 面向对象编程:使用函数模拟类与成员访问控制
本文深入解析 JavaScript 中如何使用函数模拟类,详细阐述公有成员、私有成员及特权成员的访问机制。通过闭包实现数据隐藏,利用 prototype 扩展共享方法,并探讨直接修改原型对已存在对象的影响,帮助开发者掌握 JS 面向对象编程的核心概念。
JavaScript 中类的模拟与基本概念
尽管 JavaScript 语言本身并未内置传统的类概念,但开发者可以通过函数结合闭包来模拟类的行为,包括定义类成员及私有成员(关于闭包的详细机制可参见 跨越边界: 闭包)。
跨越边界: 闭包本文将采用直观易懂的方式介绍 JavaScript 中的“类”,避免陷入过于晦涩的理论原理。
既然使用函数来模拟类,那么创建类的关键字依然是 function。下面我们将以创建一个座标点类为例进行说明。
function Point() { this.X = 0; this.Y = 0; }; var zeroPoint = new Point(); alert("当前座标值( X:" + zeroPoint.X + " , Y:" + zeroPoint.Y + " )");
公有成员与实例化机制
非静态类成员通常需要通过对象实例进行访问,因此首先需要 new 一个 Point 类型的对象,再通过该对象访问 X 和 Y 轴的坐标值。从语法层面看,JavaScript 类对象的创建过程与 C# 等语言相似,但底层实现机制存在差异。这里的 new 操作创建了一个 Object 实例,随后执行 Point() 函数时,会将该新对象的 this 指向这个新创建的 Object。Point 函数中通过 this.X 和 this.Y 定义的两个属性属于公共成员,因此 Point 的实例对象可以直接访问它们。
私有成员与特权成员的定义
讨论类成员或对象成员时,必然涉及访问权限问题。在 JavaScript 的类模拟中,同样存在 public(公有)和 private(私有)成员的概念,但具体细节有所不同。JavaScript 中的私有成员是指在类外部无法通过对象直接操作的成员;值得注意的是,即使在类内部,私有成员也不一定对所有成员可见。通常情况下,只有私有成员之间可以互相访问,其他成员因权限不足无法操作这些“隐私”数据。然而,如果拥有特权,私有与公开的界限便不再严格。私有成员变量的声明方式与普通变量一致,使用 var 关键字;私有方法既可以通过 var 声明变量来接收方法对象,也可以像普通方法那样直接构建。
function Lady() { var age = 30; var name = "菜花"; var think = function() { alert("其实我今年" + age + "岁。"); }; function fancy(){ alert("幻想变成20岁。"); }; this.Introduce = function() { alert("我叫" + name + " , 今年20岁。"); }; }; var younglady = new Lady(); alert(younglady.age);//结果undefined younglady.think(); //不支持 younglady.fancy(); //不支持上述代码展示了一个 Lady 类,其中 age、think 和 fancy 均为私有成员。think 和 fancy 方法可以访问 age 和 name,且这两个方法之间也可以互相调用。但由于它们是私有的,创建的 youngLady 对象无法直接调用 age、think 和 fancy,同样也无法访问 name。如果私有成员仅能互相调用,其存在的意义将大打折扣。为此,JavaScript 提供了特权成员机制,作为连接外部与私有成员的桥梁。特权成员属于公共成员的一种,公共成员包括普通公共成员、特权成员和对象公共成员。
特权成员是在类内部通过 this.XX 方式定义的成员。它们可以通过对象实例进行调用,同时能够访问私有成员,从而建立起访问私有成员的通道。
function Lady() { var age = 30; this.Name = "菜花"; var think = function() { alert("其实我今年" + age + "岁。"); }; function fancy() { alert("幻想变成20岁。"); }; this.Introduce = function() { alert("我叫" + this.Name + " , 今年" + age + "岁。"); }; }; var younglady = new Lady(); younglady.Introduce(); //Introduce
通过原型链添加普通公共成员
普通公共成员的创建不在类定义内部进行,而是通过类的 prototype 属性来添加。所有普通公共成员都直接添加到类的 prototype 中,而 prototype 本质上是一个类似 JSON 对象的成员集合。在创建对象时,可以理解为将类 prototype 中的成员整体拷贝到了新的 Object 实例中。
var younglady = new Lady(); younglady.Introduce(); //Introduce Lady.prototype.Hobby = "上网"; Lady.prototype.GetName = function() { return this.Name; }; var lady2 = new Lady(); alert(lady2.GetName()); alert(lady2.Hobby);上述代码通过 prototype 为 Lady 类添加了普通公共成员 GetName 方法和 Hobby 属性。由于这些是公共成员,它们可以与类定义中原本设定的特权成员互相访问。因为公共成员之间可以互相访问,我们对代码进行如下修改。
var younglady = new Lady(); Lady.prototype.Hobby = "上网"; Lady.prototype.GetName = function() { return this.Name; }; alert(younglady.GetName()); alert(younglady.Hobby);先创建 Lady 对象实例,随后再修改类成员,此时先前创建好的对象也会具备新的成员。这正是利用 prototype 作为类原型所带来的优势。简单理解,可以将 prototype 视为类对象的模板,对模板的修改会影响该类的所有对象实例。
在添加普通成员时,也可以进行批量添加,即直接用一个新的 JSON 对象赋值给 prototype。但需注意,这种操作实际上是替换了原有的 prototype 对象。在替换之前创建的对象引用的是旧的 prototype 对象,因此这些旧对象不会拥有新添加的 Hobby 和 GetName 成员。
Lady.prototype = { Hobby: "上网", GetName: function() { return this.Name; } }; var younglady = new Lady(); alert(younglady.GetName()); alert(younglady.Hobby);对象实例成员的动态添加
除了在构建类时添加公共成员,还可以直接对对象实例进行成员操作。这在本系列第二篇文章中已有描述,此处做补充说明:直接添加到对象实例上的成员也是一种公共成员,这些成员可以与类中原有的公共成员互相访问。
younglady.SetName = function(name) { this.Name = name; }; younglady.SetName("菜明"); alert(younglady.GetName());以上简要介绍了类成员的相关概念,下次将讨论类继承的相关内容。(如有不当之处,欢迎指正)
JavaScript怎么遍历对象的Symbol键和不可枚举属性
2026-11-05 13:13:39
封装一个健壮的JS字符串判空工具函数
2026-11-05 12:16:14
JavaScript合并数组的三种常用方法:concat、扩展运算符与push
2026-11-05 11:22:29
JavaScript立即执行函数(IIFE)是什么及基本语法
2026-11-05 10:51:19
JS对象数组转字符串为什么推荐用JSON.stringify()
2026-11-05 09:40:31
