授予访问内部对象,但防止外部修改?

这一直在困扰我一段时间。 我想要devise一个可以让你访问用户需要的数据的API的最好的方法,但是基于API的其他部分所依赖的一些内部对象。 这是一个简单的例子

function Person(name, ...) { this.name = name; this._friends = []; ... } Person.prototype.addFriend = function(friend){ this._friends.push(friend); }; var bob = new Person("Bob", ...); var alice = new Person("Alice", ...); var tut = new Person("Tutankhamun", ...); bob.addFriend(alice); bob.addFriend(tut); 

(我知道用_作为前缀“私有”variables并不能真正让公众隐藏起来,但我更喜欢使用原型,而不是在构造函数中重新定义函数,据我所知,你不能使用道格拉斯克洛克福德风格的私有variables与原型。我认为这是一个不同的问题,无论如何。)

现在让我们说我们想要揭示一种API用户的方式来看看谁是鲍勃的朋友。 最简单的方法是返回朋友数组:

 Person.prototype.getFriends = function() { return this._friends; }; 

但是如果一些stream氓开发者决定这样做呢?

 bob.getFriends().pop(); 

哦,可怜的图坦卡蒙 多么可怕的误会! 鲍勃仍然喜欢图坦卡蒙! 我能想到解决这个问题的唯一方法就是每次像这样克隆好友对象:

 Person.prototype.getFriends = function() { return this._friends.slice(0); }; 

但是,你必须克隆它每一次。 对于大的物体,这可能是一个问题。

那么有没有更好的方法来devise这个API来允许访问内部数据而不允许修改?

尽pipe克隆对象时存在性能下降的潜在 折衷 ,但这是迄今为止最直接和最不复杂的解决scheme。

find一个很好的实现来深入克隆一个对象,并在返回时应用。

 Person.prototype.getFriends = function() { return _.cloneDeep(this._friends); }; 

你可以使用object.freeze()来防止添加新的属性。 防止现有的属性被删除; 并防止现有属性或其可枚举性,可configuration性或可写性发生改变。

我认为你的原型应该是:

 Person.prototype.addFriend = function(friend){ this._friends.push(friend); }; 

由于_friends是一个公共财产,一个stream氓开发者仍然可以这样做:

 delete bob._friends; 

或者在许多其他方面与_friends 。 即使你模仿一个私人_friends成员,有人仍然可以覆盖getFriends方法。

底线是,JavaScript没有任何安全性,任何通过模仿私有成员来提供它的尝试都是注定的(私有成员还有其他原因,它们仍然很方便)。 在某些时候,你必须公开一个可以被修改的API。

你可以把你的整个类放入这样的闭包中。

 var Person = (function() { // this is your 'protected scope' var friends = {}; var name = ''; function Person(name, ...) { this.name = name; friends[name] = []; }; Person.prototype.addFriend = function(friend){ friends[this.name].push(friend); }; return Person; })(); 

这会阻止某人以person._friends.pop()方式访问它,但是您仍然需要克隆该对象才能返回整个数组。