JavaScript 中的变量赋值是如何工作的?

IT技术 javascript
2021-02-03 09:53:22

所以前几天我正在玩,只是为了看看在 JavaScript 中批量赋值是如何工作的。

首先我在控制台中尝试了这个例子:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

结果是“bar”显示在警报中。这是不够公平,a而且b实际上只是别名同一个对象。然后我想,我怎样才能让这个例子更简单。

a = b = 'foo';
a = 'bar';
console.log(b);

这几乎是一样的,不是吗?好吧,这一次,它返回的结果foo不像bar我对第一个示例的行为所期望的那样。

为什么会发生这种情况?

注意这个例子可以用以下代码进一步简化:

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(我怀疑 JavaScript 将诸如字符串和整数之类的原语与散列区别对待。散列返回一个指针,而“核心”原语返回其自身的副本)

6个回答

在第一个示例中,您正在设置现有对象的属性。在第二个示例中,您正在分配一个全新的对象。

a = b = {};

a并且b现在是指向同一个对象的指针。所以当你这样做时:

a.foo = 'bar';

它也设置b.fooa并且b指向同一个对象。

然而!

如果你这样做:

a = 'bar';

你是说现在a指向一个不同的对象。a对之前指出的内容没有影响

在 JavaScript 中,分配变量和分配属性是两种不同的操作。最好将变量视为指向对象的指针,当您直接分配给一个变量时,您不会修改任何对象,而只是将您的变量重新指向另一个对象。

但是分配一个属性,比如a.foo,将修改a指向的对象当然,这也会修改指向该对象的所有其他引用,因为它们都指向同一个对象。

也许字符串在技术上不是 javascript 类型“对象”,但它们可以被认为是 OO 意义上的对象。
2021-03-14 09:53:22
@ddlshack:正如 Christoph 所解释的,这是由于自动装箱。如果你是通过定义一个字符串var foo = new String('foo');,那么将是一个字符串对象(而且typeof会证实这一点)。但是如果您通过字符串文字声明它,那么它们就是字符串原语。请参阅:developer.mozilla.org/en/JavaScript/Reference/Global_Objects/...
2021-03-16 09:53:22
但是字符串是有方法和属性的,String的原型肯定是可以修改的。它们确实像物体一样。
2021-03-17 09:53:22
“你是说 a 现在指向一个不同的对象。” 不,不要使用对象这个词。字符串不是 JavaScript 中的对象。
2021-03-27 09:53:22
@Squeegy:字符串是基元,而不是对象:您不能为字符串分配任意属性!由于 Java 中所谓的自动装箱,它们的行为类似于对象
2021-04-11 09:53:22

Squeegy 已经令人满意地回答了您的问题 - 它与对象与基元无关,而是与变量的重新分配与在同一引用对象中设置属性有关。

答案和评论中似乎有很多关于 JavaScript 类型的混淆,所以这里对 JavaScript 的类型系统做一个简单的介绍:

在 JavaScript 中,有两种根本不同的值:原语和对象(没有像“哈希”这样的东西)。

字符串、数字和布尔值以及nullundefined是基元,对象是可以具有属性的一切。甚至数组和函数也是常规对象,因此可以拥有任意属性。它们只是在内部 [[Class]] 属性上有所不同(函数还有一个名为 [[Call]] 和 [[Construct]] 的属性,但嘿,这是细节)。

原始值可能表现得像对象的原因是因为自动装箱,但原始值本身不能包含任何属性。

下面是一个例子:

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

这将输出undefineda持有一个原始值,在分配属性时将其提升为对象foo但是这个新对象会立即被丢弃,因此 的值foo丢失了。

可以这样想:

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created
Mozilla 基金会的页面“JavaScript 重新介绍(JS 教程)”将 JavaScript 对象描述为“作为名称-值对的简单集合。因此,它们类似于……”,然后是字典列表,哈希、哈希表和来自各种编程语言的哈希映射。同一页面将对象属性引用描述为哈希表查找。所以对象就像一个“哈希”表。这并没有抵消其他有用的信息,但 Chris Lloyd 的原始特征并非不准确。
2021-04-06 09:53:22

您或多或少是正确的,只是您所指的“哈希”实际上只是对象的速记语法。

在第一个例子中,ab都指向同一个对象。在第二个示例中,您将a更改为引用其他内容。

这不是双重标准。在第一个示例中, a 和 b 仍然引用同一个对象,您只是修改了该对象的属性。在第二个示例中,您将 a 指向不同的对象。
2021-03-17 09:53:22
变量中有字符串还是对象都没有关系。您分配一个新的不同值,然后变量包含该新的不同值。
2021-03-19 09:53:22
那么为什么要对 Object 采取双重标准呢?
2021-03-26 09:53:22
不,区别在于在第二种情况下,您处理的是字符串,而不是对象。
2021-03-27 09:53:22
需要明确的是:这与返回自身副本的字符串无关。两个代码片段不同的原因在 Kevin 的第二段(在 Squeegy 的回答中有更完整的解释)。
2021-04-08 09:53:22

这是我的答案版本:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory

您将 a 设置为指向一个新的字符串对象,而 b 一直指向旧的字符串对象。