允许访问内部对象但阻止外部修改?

时间:2014-02-07 03:43:29

标签: javascript node.js

这一直困扰着我。我试图想出设计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);

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

现在让我们假设我们想要为API用户提供一种方式来查看谁是Bob的朋友。最简单的方法是返回friends数组:

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

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

bob.getFriends().pop();
哦,亲爱的,可怜的图坦卡蒙。多么可怕的误会!鲍勃仍然喜欢图坦卡蒙!我能想到解决这个问题的唯一方法就是每次都克隆好友对象:

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

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

那么有没有更好的方法来设计此API以允许访问内部数据而不允许修改?

4 个答案:

答案 0 :(得分:1)

虽然你是正确的,但在克隆一个对象时有一个潜在的 tradeoff性能降低,这是迄今为止最直接的&最简单的解决方案。

找一个nice implementation来深度克隆一个对象,并在返回时应用。

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

答案 1 :(得分:0)

您可以使用object.freeze()来阻止添加新属性;防止删除现有属性;并防止现有属性或其可枚举性,可配置性或可写性被更改。

答案 2 :(得分:0)

我认为你的原型应该是:

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

由于_friends是公共财产,因此流氓开发者仍然可以:

delete bob._friends;

或以许多其他方式搞乱_friends。即使您模拟私人_friends成员,仍有人可以覆盖 getFriends 方法。

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

答案 3 :(得分:0)

你可以把你的整个班级放在这样的闭包中。

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()的方式访问它,但您仍然必须克隆该对象以返回整个数组。