为什么我有一个共享缓存时,我jQuery.使用相同的缓存对象扩展两个对象

Why do I have a shared cache when I `jQuery.extend` two objects with the same caching object?

本文关键字:对象 缓存 扩展 两个 共享 有一个 jQuery 为什么      更新时间:2023-09-26

我可以用这个简单的代码片段最好地解释这个问题:

    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()?

jQuery的extend方法复制第二个对象中的所有内容,并将其放在第一个对象中。

这包括将引用复制到分配给parent._cache的数组。因此,无论何时从任何对象缓存中读取或写入,都要访问相同的数据存储。

要避免这种情况,请进行深拷贝。

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

作为题外话,因为您处理的是命名键,所以请使用Object,而不是Array。

_cache: {},  // storage var

jQuery.extend与继承无关。它将第二个对象的属性与第一个对象合并。这意味着对_cache的引用同时位于child1child2中。

阅读http://api.jquery.com/jQuery.extend/。

您得到的结果是因为在您的示例中parent_cache -成员是通过引用复制的。如果你看一下jQuery的api文档,你可以通过将true作为第一个参数传递给jQuery.extend来强制深度复制。

在这里看到一个工作的jsFiddle: http://jsfiddle.net/mLfUE/

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:[] }} )

这是关于jQuery的扩展方法,而不是Javascript内置的东西。

在本例中,您使用.extend()用父对象的属性扩展child2对象。

.extend()的jQuery文档中有一点提到:

$.extend()执行的合并在默认情况下不是递归的;

表示将parent的属性全部复制到child2中。在Javascript中,对象(也包括数组)是通过引用复制的。_cache是一个数组,所以当jQuery的extend方法将对象从parent复制到child2时,它会复制对现有_cache数组的引用,而不是复制它的所有值,所以它最终会引用与parent相同的数组。对同一数组的引用也被复制到前一行的child1中。

当通过引用复制时,引用继续指向同一个对象,使用它的任何一个引用修改该对象都会影响原始对象。