8

我正在玩 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 ??
4

5 回答 5

13

我宁愿将带有对象的变量视为指向对象的指针(如 C 指针)而不是引用。

在您的第三行中,您刚刚替换了objA,使其“指向”另一个对象。它不会改变任何objB“指向”。

在第 3 行,objA现在指向{a:2}whileobjB仍然指向您在第 2 行将objA其分配给 时所指向的任何内容objB,即{a:1}.

line 1: objA -> {a:1}
line 2: objA -> {a:1} <- objB
line 3: objA -> {a:2}, objB -> {a:1}
于 2013-10-18T11:33:52.327 回答
6

我喜欢将 JavaScript 变量视为便签。便利贴是贴在冰箱上的小便条。你可以在便签上写什么?你可以写一些小的信息。

现在 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. 我们将便签上的数字复制2x另一个便签上y,然后放在冰箱上。
  3. 我们删除便签的价值y并在上面写上数字3
  4. 现在便笺的值为 ,x便笺2y值为3

这称为按值复制,因为我们只是将便笺的值复制x到便笺上y

现在让我们以引用复制为例。实际上,让我们以引用方式复制您的示例:

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

这是您的示例中发生的事情:

  1. 我们在内存中的某处创建一个对象{a: 1},并将该对象的地址写在便笺上objAx为简单起见,我们称这个地址。
  2. 我们将地址x从便签objA复制到另一个便签上objB。现在两者都objA引用objB存储{a: 1}在内存位置的同一个对象x
  3. 因此,当我们更改objA.a相同更改的值时,会反映在objB.a因为objA两者objB都引用存储在内存位置的相同对象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指向存储在内存位置的对象say x,而p指向存储在不同内存位置的对象say y。尽管这两个对象具有完全相同的属性,但它们实际上是两个不同的对象。

例如,没有两个 Nintendo Gameboy 是相同的,无论它们看起来多么相同。本着同样的精神,让我们看一下您的最后一个示例:

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

这是上面代码中发生的事情:

  1. {a: 1}我们在内存位置创建一个对象x并将地址写在x便笺上objA
  2. x我们将地址从便笺复制objA到便笺objB。它们现在都指向存储在内存位置的同一个对象x
  3. {a: 2}我们在内存位置创建一个新对象y并将地址写在y便签上objA。现在objA有了参考价值yobjB有了参考价值x。它们引用了两个不同的对象。

正如您所看到的,分配一个新的参考值objA只是覆盖旧的参考值。它不会用 object 替换{a: 1}object {a: 2}。这在 JavaScript 中是不可能的。

于 2013-10-18T12:20:29.023 回答
2

您的第一个示例作为两个变量指向的对象是相同的

在您的第二个示例中不是因为您将另一个对象分配给objAat line #3

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

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

于 2013-10-18T11:33:58.950 回答
1

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

不,也没有“按值复制”或“按引用复制”之类的东西,只有“复制”。

变量的总是指向某个对象。对象可以是 类型Object, like {},或类型 Number , like 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

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

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

于 2013-10-18T11:50:41.880 回答
1

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

在javascript中是按值传递的变量。当涉及到对象时,它没有任何不同。该变量不是指向对象的指针,与 C++ 中的方式相同。它仅包含对只能由 javascript 本身访问的指针的引用。所以当你这样做时:

objA = objB

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

于 2013-10-18T11:37:46.317 回答