有几种方法可以解决您的问题,您选择哪一种取决于几个因素,包括 (1) 目标浏览器支持、(2) 项目目标和 (3) 个人偏好。我将重点介绍一些我特别有意见的方法。
符号
我首先强调这一点,因为它是最强大的解决方案,它是 JavaScript 的未来:它将包含在即将发布的 ECMAScript 标准版本中。今天,在符合 ES5 的浏览器中使用 shim 是可能的。这意味着它基本上适用于过去 2 年发布的所有 A 级浏览器。主要的缺点是它在 IE 8 中不受支持,但在 IE 9 和 10(当然还有所有现代版本的 FF、Chrome 和 Safari)中都受支持。如果 IE8 对您来说仍然很重要,那么这还不是您的选择。
垫片可以在这里下载:SymbolsForES5。该库使您可以开始使用此功能,并且当将来浏览器中原生包含符号时,您的代码应该能够很好地过渡。
下面是对私有成员使用符号的示例:
var x = { };
var a = new Symbol();
x[a] = 5;
console.log(x[a]); // => 5
只要您有权访问a
Symbol 对象,您就可以从该x
对象中读取属性,但如果没有访问权限的任何人都a
无法读取它。这使您可以真正拥有私人成员:
var Person = (function() {
var firstName = new Symbol(),
lastName = new Symbol();
function Person(first, last) {
this[firstName] = first;
this[lastName] = last;
}
Person.prototype.getFullName = function() {
return this[firstName] + ' ' + this[lastName];
};
return Person;
})();
var john = new Person('John', 'Smith');
john.getFullName(); // => 'John Smith'
Object.getOwnPropertyNames(john); // => [ ]
使用DON'T ACCESS字符
正如您所提到的,您始终可以在属性前面加上一个字符(例如下划线),以表明它不应该被外部代码访问。主要缺点是该物业仍然可以访问。然而,有一些好的好处:(1)高效的内存(2)使用原型继承的全部能力(3)易于使用(4)易于调试(因为属性显示在调试器中)(5)适用于所有浏览器。
我过去曾广泛使用这种方法并取得了巨大的成功。作为下划线的替代方案,在我开发的一个框架 (joi)中,我使用了该#
符号,因为它使属性更难访问(您必须使用方括号表示法来访问它),作为对任何尝试的人的温和提醒访问它,它真的很可能应该被单独留下:
function Person(first, last) {
this['#firstName'] = first;
this['#lastName'] = last;
}
Person.prototype.getFullName = function() {
return this['#firstName'] + ' ' + this['#lastName'];
};
var john = new Person('John', 'Smith');
john.getFullName(); // => 'John Smith'
我已经使用这种技术大约 7 年了,并取得了巨大的成功,如果 Symbols 不适合您的需求,我强烈推荐它。
构造函数内部的私有
我知道你说过你决定不使用这种技术是因为内存/性能的原因,但是如果你想要的东西接近真正的私有,并且你不能使用符号,因为你需要旧的浏览器支持,这是一个不错的选择。我工作的公司在大规模应用中使用这个模型,消耗的内存真的不是问题。此外,我听说一些研究发现了在此模型中优化内存和性能的方法,我相信现代浏览器(至少是 V8/Chrome,如果不是其他浏览器)开始实施这些优化。(此信息来自我从 Brendan Eich 那里听到的一次谈话;很抱歉,我不知道是哪次谈话让我一头雾水。)
最后,有专家提倡使用这种技术,虽然这不是我的偏好,但我的公司已经取得了成功,我可以保证它是一个可靠的模型。
为了完整起见,它看起来像这样:
function Person(first, last) {
this.getFullName = function() {
return first + ' ' + last;
};
}
var john = new Person('John', 'Smith');
john.getFullName(); // => 'John Smith'
JavaScript 引擎可以通过不为每个getFullName
方法真正创建一个新函数来优化这一点(即使按照书本,它应该这样做),因为在这种情况下,无法确定它是否每次都实际创建一个新函数对象。棘手的部分是,一旦引入了能够确定每次是否创建新函数对象的代码,引擎就需要切换到实际执行该操作。
弱地图
在另一个答案benvie(提到 WeakMaps)[http://stackoverflow.com/a/12955077/1662998]。如果需要 IE8 支持并且您希望属性真正模拟私有成员,我也会考虑这种替代方法。