3

此脚本的输出为: [ [ 1, 2, 3, 4 ], 10 ] [ [ 91, 92, 93, 94 ], 10 ] [ [ 8888, 8888, 8888, 8888 ], 10 ] [ [ 'one ', '二', '三', '四' ], 10 ]

但是如果我取消注释 hogs = [2, 4, 6, 8],则输出为: [ [ 1, 2, 3, 4 ], 10 ] [ [ 1, 2, 3, 4 ], 10 ] [ [ 1, 2, 3, 4 ], 10 ] [ [ 1, 2, 3, 4 ], 10 ]

如果我将其注释掉但未注释 hogs = [333, 444, 555, 666],则输出为: [ [ 1, 2, 3, 4 ], 10 ] [ [ 91, 92, 93, 94 ], 10 ] [ [ 8888, 8888, 8888, 8888 ], 10 ] [ [ 8888, 8888, 8888, 8888 ], 10 ]

我想了解简单的 javascript 闭包足以预测它们的行为。这可能缺少分析引擎的规格吗?我还想知道为什么在一个语句中重新定义整个数组与一次重新分配数组的单个值有如此截然不同的副作用。我害怕变得很有创意,因为我不知道如何预测副作用,而且我在文献中找不到线索。

var x = 10;
var hogs = [1, 2, 3, 4];
var array5 = (function () {
    var ar = [];
    var z = x;
    var w = hogs;
    function g() {
        ar.push(w);
        ar.push(z);
        return ar;
    } return g;
})()();

console.log(array5);

//hogs = [2, 4, 6, 8];  Uncommenting this prevents array5 from changing.     
x = 40;
hogs[0] = 91;
hogs[1] = 92;
hogs[2] = 93;
hogs[3] = 94;
console.log(array5);

hogs[0] = 8888;
hogs[1] = 8888;The output from this script is:
hogs[2] = 8888;
hogs[3] = 8888;
console.log(array5);

// hogs = [333, 444, 555, 666]; Un-commenting this prevents.. 

hogs[0] = 'one';
hogs[1] = 'two';
hogs[2] = 'three';
hogs[3] = 'four';
x = 40;
console.log(array5);

我不认为你在重复;当你写“你不是说'告诉变量 w 引用任何对象引用”时,你优雅地概括了整个事情。您所说的是“告诉变量 w 引用当前所引用的任何对象”您很乐意花时间详细解释这一点。也许您对学习该语言的人表示同情,他们简单地想象对变量值的搜索在必要时通过范围链一直向外进行到 Object 对象,而在这种情况下,封闭函数中的值被忽略,直到全局中的值范围被一个新的定义所抹杀。很高兴知道更改数组中的值会维护数组本身,完好无损,但有一些新的或改变的价值分配;这与重新定义数组非常不同,使变量指向全新的东西。记忆中一些物体静止不动的图像,而另一些则消失并重新出现在记忆中的其他地方,在我脑海中翩翩起舞;与“睡觉的时间”押韵。对于可能对此感兴趣的任何其他人,所有对象似乎都存在类似的情况,如以下代码所示:

enter code here
var x = 10;
var hogs = {'a': 1, 'b': 2, 'c' : 3, 'd' : 4};
var obj = {};
obj.h = hogs;

var ob5 = (function (arg) {
    function g() {
        var w = arg;
        return w;
    } return g;
})(hogs)();

console.log(ob5);

//hogs = [2, 4, 6, 8];  Uncommenting this prevents array5 from  changing.
x = 40;
hogs['a'] = 91;
hogs['b'] = 92;
hogs['c'] = 93;
hogs['d'] = 94;
console.log(ob5);

hogs.a = 8888;
hogs.b = 8888;
hogs.c = 8888;
hogs.d = 8888;
console.log(ob5);

hogs = {'a':333, 'b':444, 'c':555, 'd':666}; // Prevents further changes.

hogs.a = 'one';
hogs.b = 'two';
hogs.c = 'three';
hogs.d = 'four';
x = 40;
console.log(ob5);
4

2 回答 2

3

闭包返回应用(调用)时的当前值hogs如果您hogs稍后更新以指向新数组,array5(调用闭包的结果)仍然引用旧副本。您的更新仅反映在 的当前副本中hogs,因此无论您更改什么位置,都将反映您的更新array5

您可以再次应用闭包,您将获得一个反映 的当前值的新值hogs,但您必须为其分配一个名称(将其放入变量中)以再次引用它。


这就是我的想法,也是我预料到的。但是除非我包含(取消注释)以下行,否则我不会得到预测的行为:hogs = [2, 4, 6, 8]。这会将array5 冻结在“hogs = [2, 4, 6, 8]”之前的值。否则,array5 就像对 hogs 的当前值的引用。我不知道这种行为是如何被预测的,我也无法在事后解释它。这对你有意义吗?

是的,这就是你应该期望它的行为方式。数组是可变对象,因此在一个地方更改数组将反映在您更改它的其他任何地方。你在做什么真的和这没什么不同:

var x = [1, 2, 3];
var y = x;
var x[0] = 4;
// What would you expect the value of y to be here?

如果您真的想拥有数组的副本,那么您应该使用以下slice()方法进行显式复制:

var array5 = (function () {
    var ar = [];
    var z = x;
    var w = hogs.slice(); // <== Explicit copy!
    function g() {
        ar.push(w);
        ar.push(z);
        return ar;
    } return g;
})()();
于 2013-09-06T23:40:54.410 回答
1

这是冗长且重复的,但我尝试以几种不同的方式解释它,以便您理解它:

内存中有一个值为 [1,2,3,4] 的数组。访问这个数组的方法是通过它的名字“hogs”。

闭包中的变量 w 也指向内存中的同一个数组,但它通过名称 w 指向它。所以现在有两种方法可以访问内存中的同一个数组,具体取决于您所处的范围 - 在闭包之外,您要求“hogs”,在闭包内,您要求“w” - 在任何一种情况下,你都会得到返回相同的对象 - [1,2,3,4] 的数组

如果您将“hogs”设置为另一个数组 - 即 [2,4,6,8] - 原始数组 - 即 [1,2,3,4] - 仍然存在于内存中,但它不能再被访问“hogs” 它只能在闭包内通过询问“w”来访问。

所以,当你有线路时

hogs = [2,4,6,8]

注释掉,你做

 hogs[0] = 91

您正在将内存中数组 [1,2,3,4] 的第一项(hogs 和 w 都指向)更改为 91。此时,w(闭包内)和 hogs(闭包外)是仍然引用内存中的相同数组

如果您取消注释该行

hogs = [2,4,6,8]

那么 hogs 和 w 现在引用了两个不同的数组 - hogs 引用了一个值为 [2,4,6,8] 的数组,而 w 引用了一个值为 [1,2,3,4] 的不同数组。

那么当你说

hogs[0] = 91

hogs 现在引用一个如下所示的数组:

hogs = [91,4,6,8]

但是 w,因为它没有引用与 hogs 相同的数组,所以仍然包含以下值:

w = [1,2,3,4]

如果您在这一点上和我在一起 - 那么只需按原样阅读其余代码。如果 w 现在引用 [1,2,3,4] 的数组,那么通过读取闭包中的代码,w 的值永远不会改变(因为无法从闭包外部访问它)。因此,您将永远无法更改返回的值array5- 这就是为什么您可以在更改数组的值时一遍又一遍地调用它,hogs但您总是会返回相同的对象 - 因为hogs现在引用不同的数组当您更改其中的值时,您永远w不会更改其中的任何值whogs

这是我能做的最好的。

编辑

我想这里的关键是在这条线上说:

var w = hogs

不是说“告诉变量 w 引用任何对象引用”。您所说的是“告诉变量 w 引用当前引用的任何对象”

因此,当您告诉hogs引用不同的对象(数组 - [2,4,6,8])时,w仍然保留对hogs原始引用的对象的引用(数组 - 1,2,3,4])

抱歉重复了。

最后,我猜原来的评论者是正确的——这与其说是一个闭包概念,不如说是一个变量引用概念。

于 2013-09-07T13:30:48.763 回答