老实说,我发现为了在 JS 中强制执行真正的隐私需要做出太多牺牲(除非您正在定义一个模块),所以我更喜欢仅依赖命名约定,例如this._myPrivateVariable
.
这对任何开发人员来说都是一个明确的指示,他们不应该直接访问或修改这个成员,也不需要牺牲使用原型的好处。
如果您需要将您的size
成员作为属性访问,您将别无选择,只能在原型上定义一个 getter。
function MyObj() {
this._size = 0;
}
MyObj.prototype = {
constructor: MyObj,
incrementSize: function () {
this._size++;
},
get size() { return this._size; }
};
var o = new MyObj();
o.size; //0
o.size = 10;
o.size; //0
o.incrementSize();
o.size; //1
我见过的另一种方法是使用模块模式来创建一个privates
对象映射,该映射将保存各个实例的私有变量。实例化时,会在实例上分配一个只读私钥,然后使用该密钥从privates
对象中设置或检索值。
var MyObj = (function () {
var privates = {}, key = 0;
function initPrivateScopeFor(o) {
Object.defineProperty(o, '_privateKey', { value: key++ });
privates[o._privateKey] = {};
}
function MyObj() {
initPrivateScopeFor(this);
privates[this._privateKey].size = 0;
}
MyObj.prototype = {
constructor: MyObj,
incrementSize: function () { privates[this._privateKey].size++; },
get size() { return privates[this._privateKey].size; }
};
return MyObj;
})();
您可能已经注意到,这种模式很有趣,但上述实现存在缺陷,因为即使没有对持有密钥的实例对象的引用,私有变量也永远不会被垃圾回收。
然而,有了 ES6 WeakMap,这个问题就消失了,它甚至简化了设计,因为我们可以使用对象实例作为键,而不是像上面那样使用数字。如果实例被垃圾回收,weakmap 将不会阻止对该对象引用的值的垃圾回收。