JS对象按值复制与按引用复制

时间:2013-10-18 11:31:11

标签: javascript

我正在玩Chrome控制台并注意到一些我无法理解的东西。我知道JS变量是按值复制的,对象是通过引用复制的。下面的代码工作正常,输出2并证明JS对象作为参考:

var objA = {a: 1};
var objB = objA;
objA.a = 2; 
objB.a; // 2

但是这段代码不能正常工作。我希望objB.a输出2但它会改为1。为什么?

var objA = {a: 1};
var objB = objA;
objA = {a: 2};  //Assigned whole object here instead property.
objB.a; //1 - Shouldn't this be 2 ??

5 个答案:

答案 0 :(得分:11)

我宁愿把对象变量看作指向对象的指针(比如C指针)而不是引用。

在第三行中,您刚刚替换了objA,使其“指向”另一个对象。无论objB是“指向”,都不会改变

第3行,objA现在指向{a:2},而objB仍然指向objA指向您objB时指向的任何内容,在第2行,即{a:1}

line 1: objA -> {a:1}
line 2: objA -> {a:1} <- objB
line 3: objA -> {a:2}, objB -> {a:1}

答案 1 :(得分:5)

我喜欢将JavaScript变量视为sticky notes。粘滞便笺是放在冰箱上的小纸条。你能用粘滞便笺写什么?你可以写一小段信息。

现在JavaScript中有两种类型的信息 - 原始值和参考值。原始值是您可以直接在便签上写下的一小段信息。它们包括:

  1. 布尔
  2. 数字
  3. 字符串
  4. 未定义
  5. 另一方面,参考值是大量的信息,您无法在小便利贴上书写。那么如何在粘滞便笺中存储参考值?

    你没有。

    参考值(如数组,对象和函数)写在更大的纸上,只有对它们的引用才会写在便签上。例如,我的妻子可能写道:

      

    亲爱的,杂货清单在你的键盘下面。

    这里的杂货清单是一个数组(即大量的信息)。因为我们不能用一个小的便条写它,我们只需将它写在一张更大的纸上,并制作一个粘滞便笺,告诉我们它在哪里被发现。在编程方面:

    var groceryList = ["1 apple", "2 bananas", "3 loaves of bread"];
    

    这里实际的购物清单存储在内存中的某处,只有购物清单的地址存储在变量groceryList中。


    那么当我们将一个变量分配给另一个变量时会发生什么?让我们首先举一个原始值的例子:

    var x = 2;
    var y = x;
    alert(y);  // 2
    y = 3;
    alert(x);  // 2
    

    这就是发生的事情:

    1. 我们在新的便签上写下2号码并将其放在我们的冰箱上。
    2. 我们将粘滞便笺2中的号码x复制到另一张便条y上并将其放在我们的冰箱上。
    3. 我们会删除便签y的值,并在其上写上3号。
    4. 现在,便笺x的值为2,便笺y3
    5. 这称为按值复制,因为我们只是将便签x的值复制到便利贴y上。

      现在让我们举一个引用副本的例子。事实上,让我们以你的参考为例进行复制:

      var objA = {a: 1};
      var objB = objA;
      objA.a = 2;
      objB.a; // 2
      

      以下是您的示例中发生的事情:

      1. 我们在内存中的某处创建一个对象{a: 1},并在便签objA上写下此对象的地址。为简单起见,我们将此地址称为x
      2. 我们将地址x从便笺objA复制到另一个便笺objB。现在,objAobjB都引用了存储在内存位置{a: 1}的同一个对象x
      3. 因此,当我们更改objA.a的值时,相同的更改会反映在objB.a上,因为objAobjB都引用存储在内存位置{{1 }}。
      4. 这称为复制引用,因为我们只是将对象的引用从便签x复制到便签objA。我们没有复制实际的对象。

        那么按引用复制和按值复制有什么区别?绝对没有。在这两种情况下,我们只是将一个便利贴的值复制到另一个便利贴上。

        只有当它们包含完全相同的信息时才会说两个便利贴。例如,以下内容是等效的:

        objB

        但是以下值并不相同:

        var x = 2;
        var y = 2;
        alert(x === y); // true
        
        var o = {a: 1};
        var p = o;
        alert(o === p); // true
        

        它们不相等的原因是因为var o = {a: 1}; var p = {a: 1}; alert(o === p); // false 指向存储在内存位置的对象说o,而x指向存储在不同内存位置的对象说{ {1}}。虽然这两个对象具有完全相同的属性,但它们实际上是两个不同的对象。

        例如,无论两个Nintendo Gameboys看起来多么相同,都没有两个相同。以同样的精神让我们来看看你的最后一个例子:

        p

        以上是代码中发生的事情:

        1. 我们在内存位置y创建一个对象var objA = {a: 1}; var objB = objA; objA = {a: 2}; //Assigned whole object here instead property. objB.a; //1 - Shouldn't this be 2 ?? ,并在便利贴{a: 1}上写下地址x
        2. 我们将地址x从便利贴objA复制到便签x。现在,它们都指向存储在内存位置objA的相同对象。
        3. 我们在内存位置objB创建一个新对象x,并在便条贴{a: 2}上写下地址y。现在y的参考值为objAobjA的参考值为y。他们引用了两个不同的对象。
        4. 如您所见,为objB分配新的参考值只会覆盖旧的参考值。它不会将对象x替换为对象objA。这在JavaScript中是不可能的。

答案 2 :(得分:2)

您的第一个示例适用于两个变量指向的对象

在第二个示例中,不是因为您要在objA另一个对象分配给line #3

objA = {a: 2};  //Assigned whole object here instead property.

这会使objA指向另一个对象({a:2}),而objB将指向旧对象。

答案 3 :(得分:1)

在你的情况下,ObjB并没有指向变量objA,而是指向varable objA指向的对象,因此更改对象属性与更改变量指向的位置不同。

在javascript中是按值传递的变量。对于物体而言,它没有任何不同。变量不是指向对象的指针,就像在C ++中一样。它只包含对只能由javascript本身访问的指针的引用。所以当你这样做时:

objA = objB

你只能将引用指针复制到内存中的对象。

答案 4 :(得分:1)

  

我正在玩Chrome控制台并注意到一些我无法理解的东西。我知道JS变量是按值复制的,对象是通过引用复制的。

没有。也没有“按值复制”或“按引用复制”这样的东西,只是“复制”。

变量的总是指某个对象。对象可以是Object类型,如{},也可以是Number类型,如5或任何其他类型。

分配和函数调用从不在Javascript中复制对象,它只将相同的值绑定到另一个变量:

var a = {},  b = 5;
var a1 = a,  b1 = b;
// variables a and a1 refer to the same object, {}
// variables b and b2 refer to the same object, 5

a['x'] = 10; // the object referred to by a and a1 is modified
a = {'x': 10} // now a refers to a new object and a1 is unaffected

b += 10; // b and b1 now point to a different objects, 15 and 5

function foo(x) {
    ...
}
foo(a); // x inside foo is the same object, {}
foo(b); // x inside foo is the same object, 5

复制对象必须由explicitly完成,对象不会被神奇地复制。

复制Object是有道理的,因为可以修改Object。但是,复制数字或字符串没有意义 - 你只关心变量的值是指1234,但你永远不会关心“哪个特定的1234”。 (他们没有“身份”。)