JavaScript 中的私有成员:公有、私有与特权模式详解

作者:小括号

时间:2026-10-21

浏览:332

本文深入解析 JavaScript 中私有成员的实现机制,澄清对象无真正私有变量的误解。通过构造器、原型链与闭包特性,详细演示如何创建公有、私有及特权成员,并探讨其内存管理与访问限制,帮助开发者构建更严谨的面向对象应用。

文章目录

JavaScript 常被视为一门被严重误解的语言。一种常见观点认为,由于 JavaScript 对象缺乏真正的私有变量和方法,因此它不具备“信息隐藏”能力。然而,这其实是一种误解。JavaScript 对象完全可以拥有私有成员,接下来我们将探讨具体实现方式。(SharkUI.com 注:JavaScript 并非原生支持面向对象中的私有、公有等特性,本文所述的私有、公有及特权成员,实际上是借助 JavaScript 的其他特性(详见“闭包”章节)“模拟”实现的。感兴趣者可搜索相关文献深入了解,当然,若将其当作真正的面向对象特性使用也未尝不可。祝阅读愉快!)

对象

JavaScript 的核心建立在对象之上。数组(Array)、函数(Function)乃至对象(Objects)本身,皆属于对象范畴。那么,究竟什么是对象?对象本质上是一组“名称:值”对的集合。其中,名称必须是字符串,而值可以是字符串、数值、布尔值或另一个对象(包括数组和函数)。为了支持快速取值,对象通常基于哈希表实现。

当某个值是一个函数时,我们可以将其视为“方法”。在调用对象的方法时,变量 this 会被绑定为该对象本身。这使得方法能够通过 this 变量访问该对象的实例数据。

对象可以通过“构造器(constructor)”来创建。构造器是一个用于初始化对象的函数。它提供了类似其他编程语言中“类(class)”所具备的特性与功能,包括静态变量和方法的支持。

公有

对象的所有成员默认均为公有成员。任何函数都可以访问、修改或删除这些成员,甚至可以动态添加新成员。向对象添加成员主要有两种途径:

通过构造器

这种方法通常用于初始化对象实例的公有变量。我们利用构造器中的 this 变量来为对象添加成员:

function Container(param) {
  this.member = param;
}

创建一个新对象:

var myContainer = new Container('abc');

此时,公有变量 myContainer.member 即被赋予值 'abc'。

通过原型(prototype)

此方法通常用于添加公有方法。当在对象本身中未找到指定成员时,JavaScript 会查找构造器的原型(prototype)成员。这种原型机制实现了面向对象概念中的“继承(inheritance)”,同时有效节省了内存。若需为同一构造器创建的所有对象添加一个方法,只需将该函数赋值给构造器的 prototype:

Container.prototype.stamp = function (string) {
  return this.member + string;
}

随后,我们可以调用该方法:

myContainer.stamp('def')

该方法将返回 'abcdef'。

私有

私有(Private)成员由构造器内部创建。通常,在构造器中使用 var 声明的变量以及函数参数,均成为私有成员。

function Container(param) {
  this.member = param;
  var secret = 3;
  var self = this;
}

该构造器创建了三个私有的实例变量:param、secret 和 self。

function Container(param) {
  function dec() {
   if (secret > 0) {
     secret -= 1;
     return true;
   } else {
     return false;
   }
  }

  this.member = param;
  var secret = 3;
  var self = this;
}

私有方法 dec 用于检查实例变量 secret。若 secret 大于 0,则将其自减 1 并返回 true;若小于等于 0,则返回 false。由此实现了该构造器创建的对象中 dec 函数仅能被调用三次的功能。

按照惯例,我们创建了一个私有变量 self。私有方法可通过它来访问对象本身。但这仅是一种权宜之计,因为《ECMAScript 语言规范》中存在一个错误,导致内部函数的 this 变量被设置为错误的值。

公有方法(SharkUI.com 注:即上文所述通过 prototype 创建的方法)无法直接调用私有方法。因此,为了使用私有方法,我们需要引入特权方法(privileged method)。

特权

特权方法可以访问私有变量和方法,同时它本身也可被公有方法和外部代码访问。你可以删除或替换一个特权方法,但不能修改它,也不能强制它放弃其保密性(SharkUI.com 注:原文如此,可能指其特权属性,具体细节请资深开发者指正)。

特权方法是在构造器内部通过 this 创建的。

function Container(param) {
  function dec() {
   if (secret > 0) {
     secret -= 1;
     return true;
   } else {
     return false;
   }
  }

  this.member = param;
  var secret = 3;
  var self = this;

  this.service = function () {
   if (dec()) {
     return self.member;
   } else {
     return null;
   }
  };
}

service 是一个特权方法。前三次调用 myContainer.service() 将返回 'abc',之后将返回 null。service 通过调用私有方法 dec 来访问私有变量 secret。对于其他对象和方法而言,可以访问 service,但无法直接访问私有成员。

闭包

公有、私有和特权成员模式的存在,源于 JavaScript 的内在机制:闭包。这意味着内部函数永远可以访问其外部函数的变量和参数,即使外部函数已经返回。这是 JavaScript 语言非常强大的一个特性。目前尚无 JavaScript 编程书籍展示如何利用它,甚至许多书籍对此只字未提。

私有和特权成员只能在对象初始化时创建,而公有成员可以随时添加。

模式

公有
function Constructor(...) {
  this.membername = value;
}
Constructor.prototype.membername = value;
私有
function Constructor(...) {
  var self = this;
  var membername = value;
  function membername(...) {...}
}

注:这句代码:

function membername(...) {...}

事实上是以下代码的简略写法

var membername = function membername(...) {...};
特权
function Constructor(...) {
  this.membername = function (...) {...};
}

后记

Douglas Crockford 的这篇文章为我们编写更优美的 JavaScript 程序奠定了基础,使得创建更合理的面向对象应用和框架成为可能。在本文翻译即将完成时,惊讶地发现作者网站上已出现本文中文版的链接。这是一件好事!越来越多的中国人开始关注这些“边边角角”的技术。虽然做了重复工作,但仍希望各位能从这篇文章中有所收获。同时也希望有更多人投身于前端技术文章的原创与翻译中,在多数人浮躁的时候,我们需要更多基础性工作。一周一篇不多,一年一篇不少,只要开始了就行!

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章