2011-10-09 69 views
1

我可以用這個簡單的代碼片段最好的解釋這個問題:爲什麼我在使用相同緩存對象的`jQuery.extend`兩個對象時有共享緩存?

var child1 = {name: 'child1'}; 
    var child2 = {name: 'child2'}; 

    var parent = { 
     _cache: [], // storage var 
     writeCache: function(key, val) 
     { 
      console.log('writing cache::'+this.name); 
      this._cache[key] = val; 
     }, 
     readCache: function(key) 
     { 
      if(this._cache[key] == undefined) 
      { 
       return false; 
      } 
      return this._cache[key]; 
     }, 
    }; 
    jQuery.extend(child1, parent); 
    jQuery.extend(child2, parent); 

    child1.writeCache('myKey', 123); 

    console.log(child1.readCache('myKey')); // returns 123 as expected 

    console.log(child2.readCache('myKey')); // returns 123 unexpectedly (for me at least) 

見最後一行:

console.log(child2.readCache('myKey')); 

現在爲什麼它會返回123的時候我們只訪問child1的writeCache()?

+1

這看起來像是關於jQuery的擴展方法,而不是Javascript的繼承。 – thomasrutter

回答

4

jQuery的擴展方法複製第二個對象中的所有內容並將其放入第一個對象中。

這包括將參考複製到您分配給parent._cache的陣列。因此,無論何時從任何對象緩存中讀取或寫入,都可以訪問相同的數據存儲。

要避免這種情況,請進行深層複製。

jQuery.extend(true, child1, parent); 
jQuery.extend(true, child2, parent); 

另外,由於您使用的是命名鍵,因此請使用Object而不是Array。

_cache: {}, // storage var 
+0

太棒了,它的工作!感謝您的詳細解釋。很有幫助! –

1

parent_cache被複制到兩個子對象。所以基本上,將出現以下情況:

child1._cache = parent._cache 
child2._cache = parent._cache 

但現在它們都引用在存儲器中的相同陣列(JS通過相同的附圖)。所以當你改變一個,你應該期望它被反映到其他地方。例如:

parent = {_cache:[]} 
child1 = {} 
child2 = {} 

child1._cache = parent._cache 
child2._cache = parent._cache 

child1._cache.push(9) 
child2._cache; // [9] 

你可以用原型繼承解決這個問題:

function parent(){ 
    this._cache = []; 
} 
parent.prototype.writeCache = ... 
parent.prototype.readCache = ... 

child1 = new parent(); 
child2 = new parent(); 

child1.writeCache('myKey', 123); 

console.log(child1.readCache('myKey')); // 123 
console.log(child2.readCache('myKey')); // undefined (/false in your case) 

你也可以使用Object.create與原代碼:

child1 = Object.create(parent, {_cache: { value:[] }}) 
child2 = Object.create(parent, {_cache: { value:[] }}) 
+0

感謝您的回答。但看看昆汀的建議。非常簡單的解決方案,無需修改大量代碼。 –

+0

@AbhimanyuGrover,這是真的。我只是不認爲使用jQuery的擴展是一個很好的繼承模式。它在內存使用方面肯定不比我的原型解決方案好,因爲它將這些功能複製給每個孩子,而原型則不是。 – davin

1

jQuery.extend無關與繼承。它將第二個對象的屬性合併到第一個對象的屬性中。這意味着對_cache的引用都在child1child2之間。

閱讀http://api.jquery.com/jQuery.extend/

1

你會得到結果,因爲_cache - parent的成員在您的示例中被引用複製。如果您查看jQuery的API文檔,您可以通過將true作爲第一個參數傳遞給jQuery.extend來強制進行深層複製。

看到一個工作的jsfiddle這裏:http://jsfiddle.net/mLfUE/

0

這是關於jQuery's extend method,而不是建立在爲Javascript的東西。

在這種情況下,您正在使用.extend()擴展具有父對象屬性的child2對象。

jQuery文檔。擴展()在一點提到:

默認情況下,由$ .extend()執行的合併不是遞歸的;

這將表明父代的屬性被全部複製到child2中。在Javascript中,對象(因此也是數組)通過引用被複制。 _cache是​​一個數組,因此當jQuery的extend方法將父對象複製到child2中時,它會將引用複製到現有的_cache數組中,而不是複製其所有值,因此它最後會引用與父數組相同的數組。對同一陣列的引用也被複制到先前行中的child1中。

按引用複製時,引用繼續指向同一對象,並且使用其任何引用修改該對象都會影響原始對象。