2011-10-09 74 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中。

按引用复制时,引用继续指向同一对象,并且使用其任何引用修改该对象都会影响原始对象。