2014-02-07 88 views
1

這一直在困擾我一段時間。我正在設法考慮設計一個API的最佳方式,該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用戶看到誰是鮑勃的朋友。最簡單的方法是返回朋友陣列:

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

但是如果一些流氓開發者決定這樣做呢?

bob.getFriends().pop(); 

哦,親愛的,可憐的圖坦卡蒙。多麼可怕的誤會!鮑勃仍然喜歡圖坦卡蒙!我能想到的解決這個問題的唯一辦法是隻克隆對象的朋友一樣,每次這樣:

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

但你必須克隆它每一次。對於大型物體,這可能是一個問題。

那麼有沒有更好的方法來設計這個API來允許訪問內部數據而不允許修改?

回答

1

雖然你是正確的,有克隆的對象時的性能降低一個潛在tradeoff,這是迄今爲止最直接的&最不復雜的解決方案。

找到一個nice implementation用於深度克隆對象,並在返回時應用。

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

您可以使用object.freeze()防止添加新屬性;防止現有屬性被刪除;並防止改變現有屬性或其可枚舉性,可配置性或可寫性。

+0

[object.seal()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal)可能是一個更好的候選人,因爲他可能還想要修改現有的屬性,但防止添加/刪除。 – adamb

+0

如果凍結對象,則不能在內部修改對象。密封可以防止你在這個例子中添加好友,這是一個有效的公共方法。 –

0

我覺得你的原型應該是:

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

而且由於_friends是公共財產,一個無賴開發商仍然可以做:

delete bob._friends; 

或惹_friends在許多其他方法。即使您模仿私人_friends成員,仍有人可以覆蓋getFriends方法。

底線是JavaScript沒有任何安全性,任何試圖通過模擬私人成員來提供它的嘗試都是註定的(私人成員還有其他原因,他們仍然很方便)。在某些時候,你必須公開一個可以修改的API。

+0

我不關心_friends的公衆可見度。我想確保getFriends的返回值不會被意外修改,或者至少表明它不應該被修改。 –

+0

在這種情況下,您必須依賴記錄返回的數組是** ** **好友數組,並且可能提供返回副本的* getFriendsCopy *方法。或者提供一些方法去處理任何人可能想要做的事情* _friends *,這樣他們就不會直接修改它(getters/setters)。 – RobG

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()時尚訪問它,但你還是要克隆的對象返回整個數組。

+0

是的,這是正確的,我衝了一下。但是這種情況可以考慮在內。 –

+0

看起來Rahat擔心公共可用方法返回不應該被修改的對象。沒有命名約定表示不應修改getFriends的返回值。也許JSDoc可以提供一些幫助,但實施它的唯一方法是返回對象的副本。 – HMR

+0

現在你的代碼泄漏了,當一個Person實例超出範圍時,這個朋友數組並沒有遵循,也從來沒有被垃圾回收。這也取決於所有人員的姓名。你可以通過實現一個id來幫助你,但仍然會泄漏。我認爲最好遠離那些模仿僅僅是爲了防止不小心的人犯錯的東西的模式。在我看來,下方不會超出上方 – HMR

相關問題