我喜欢将 JavaScript 变量视为便签。便利贴是贴在冰箱上的小便条。你可以在便签上写什么?你可以写一些小的信息。
现在 JavaScript 中有两种类型的信息——原始值和引用值。原始值是您可以直接写在便签上的一小段信息。他们包括:
- 布尔值
- 数字
- 字符串
- 无效的
- 不明确的
另一方面,参考值是您无法在小便签上写下的大量信息。那么如何在便签中存储参考值呢?
你没有。
参考值(如数组、对象和函数)写在一张更大的纸上,并且只有对它们的引用才写在便签上。例如,我的妻子可能会写:
亲爱的,杂货清单在你的键盘下面。
这里的杂货清单是一个数组(即大量信息)。因为我们不能把它写在一张小的便签上,所以我们只需将它写在一张更大的纸上,然后做一个便签,告诉我们在哪里可以找到它。在编程方面:
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
这就是正在发生的事情:
- 我们将号码写
2
在一张新的便签上,然后贴在冰箱上。
- 我们将便签上的数字复制
2
到x
另一个便签上y
,然后放在冰箱上。
- 我们删除便签的价值
y
并在上面写上数字3
。
- 现在便笺的值为 ,
x
便笺2
的y
值为3
。
这称为按值复制,因为我们只是将便笺的值复制x
到便笺上y
。
现在让我们以引用复制为例。实际上,让我们以引用方式复制您的示例:
var objA = {a: 1};
var objB = objA;
objA.a = 2;
objB.a; // 2
这是您的示例中发生的事情:
- 我们在内存中的某处创建一个对象
{a: 1}
,并将该对象的地址写在便笺上objA
。x
为简单起见,我们称这个地址。
- 我们将地址
x
从便签objA
复制到另一个便签上objB
。现在两者都objA
引用objB
存储{a: 1}
在内存位置的同一个对象x
。
- 因此,当我们更改
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 ??
这是上面代码中发生的事情:
{a: 1}
我们在内存位置创建一个对象x
并将地址写在x
便笺上objA
。
x
我们将地址从便笺复制objA
到便笺objB
。它们现在都指向存储在内存位置的同一个对象x
。
{a: 2}
我们在内存位置创建一个新对象y
并将地址写在y
便签上objA
。现在objA
有了参考价值y
,objB
有了参考价值x
。它们引用了两个不同的对象。
正如您所看到的,分配一个新的参考值objA
只是覆盖旧的参考值。它不会用 object 替换{a: 1}
object {a: 2}
。这在 JavaScript 中是不可能的。