使用Javascript数组方法的意外行为

时间:2011-02-21 06:39:32

标签: javascript

采取以下代码

var a = b = [];
a.push('value');
if (a === b) {
    console.log('a === b'); // this will execute
}

console.log(a, b); // ["value"] ["value"]

是什么给出的?为什么ab都被修改了? var声明ba的实时副本吗?如果是这样,那么为什么不能同样适用于常规变量赋值,例如:

var a = b = '';
a = 'value';
if (a === b) {
    console.log('a === b'); // this will never run
}

console.log(a, b); // value

当然,使用以下声明解决了初始示例中的问题:

var a = [], b = [];

但是最初例子中的行为让我感到奇怪,特别是它只适用于数组操作。

如果有帮助,我使用的是Google Chrome 10.0.648.82测试版

3 个答案:

答案 0 :(得分:6)

grok需要的概念是引用。当您将一个变量分配给指向ECMAScript中的对象([]new Array{}new Object,函数等)的另一个变量时,参考是通过。除非您创建新对象并将其分配给b,否则a会引用b

重申一下,var a = [], b = []会创建两个不同的数组。 var a = b = []b分配给[],然后b分配给a,{{1}}选择存储在内存中的相同确切对象。任何mutator方法都会改变该对象,分配给该对象的任何变量都将引用该对象。

答案 1 :(得分:4)

当您在变量中存储“对象”(包括数组)时,您实际存储的是对象的引用(或“指针”),而不是对象的副本对象本身。也就是说,对象存在于内存中,并且存储在变量中的是一条信息,告诉解释器在哪里找到该对象。

相比之下,数字或布尔等基元实际上存储在变量。

所以在我最好的ASCII艺术中,当你有代码

var y = [];

...你最终得到了这个:

+−−−−−−−−−−+            
|     y    |            
+−−−−−−−−−−+            +−−−−−−−−−−−−−−−−−−+
| Ref55467 |−−−−−−−−−−−>| The actual array |
+−−−−−−−−−−+            +−−−−−−−−−−−−−−−−−−+
                        | []               |
                        +−−−−−−−−−−−−−−−−−−+

(显然,Ref55467只是一个例子,你实际上永远无法在代码中看到对象引用的基础值。)

......而与:

var x = 32;

...你最终得到:

+−−−−−−−−−−+
|     x    |
+−−−−−−−−−−+
|       32 |
+−−−−−−−−−−+

现在让我们来看看你的

var a = b = [];

看起来像这样:

+−−−−−−−−−−+
|     a    |
+−−−−−−−−−−+
| Ref55467 |−−−−−−+     
+−−−−−−−−−−+      |     
                  |     +−−−−−−−−−−−−−−−−−−+
                  +−−−−>| The actual array |
                  |     +−−−−−−−−−−−−−−−−−−+
+−−−−−−−−−−+      |     | []               |
|     b    |      |     +−−−−−−−−−−−−−−−−−−+
+−−−−−−−−−−+      |
| Ref55467 |−−−−−−+
+−−−−−−−−−−+

ab引用(指向)内存中的相同的数组,因此更改数组的操作会更改数组,无论您使用哪个引用调用它们。

与之相反:

var a = b = 32;

....看起来像:

+−−−−−−−−−−+
|     a    |
+−−−−−−−−−−+
|       32 |
+−−−−−−−−−−+

+−−−−−−−−−−+
|     b    |
+−−−−−−−−−−+
|       32 |
+−−−−−−−−−−+

数字32实际存储在每个地方,而不是两个变量指向的中心位置。

将值传递给函数时也是如此。使用对象引用时,传递给函数的值是对象引用。您实际上并没有将对象传递给函数,只是对它的引用。这意味着如果函数使用引用来操作对象,则会在调用代码中看到结果。例如:

function foo(theArray) {
    theArray[0] = 1;
}

var a = ["one", "two", "three"];
foo(a);
alert(a[0]); // alerts "1"

当我们调用foo时,我们将引用传递给数组对象,因此当foo使用它来修改数组时,我们会看到结果。< / p>

答案 2 :(得分:2)

它不是实时副本,它是引用同一实例的对象。

当您运行[]时,您正在执行new Array(),因此,您要将新的Array实例分配给ab

祝你好运!