变量赋值如何在JavaScript中起作用?


97

因此,前几天我在玩,目的是确切地了解批量分配在JavaScript中的工作方式。

首先,我在控制台中尝试了以下示例:

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

结果是在警报中显示“栏”。这很公平,a而且b实际上只是同一对象的别名。然后我想,如何使这个例子更简单。

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

那几乎是同一回事,不是吗?好吧,这次返回的结果与我从第一个示例的行为所期望的foobar一样。

为什么会这样?

注意:可以使用以下代码进一步简化此示例:

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

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

(我怀疑JavaScript与哈希将对字符串和整数之类的基元的处理方式不同。哈希返回一个指针,而“核心”基元返回其自身的副本)


Answers:


114

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

a = b = {};

a并且b现在是指向同一对象的指针。因此,当您这样做时:

a.foo = 'bar';

b.foo从此它也设置ab指向同一对象。

然而!

如果您改为这样做:

a = 'bar';

您是说现在a指向另一个对象。这a对之前指出的没有影响。

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

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


3
“您说的是现在指向另一个对象。” 不,不要使用对象一词。字符串不是JavaScript中的对象。
Nosredna

9
从技术上讲,字符串可能不是javascript类型的“对象”,但从面向对象的角度来看,它们可以被认为是对象。
亚历克斯韦恩

2
@Squeegy:字符串是基元,而不是对象:您不能为字符串分配任意属性!他们只对象的行为类似,因为所谓的自动装箱在Java中
克里斯托夫

11
但是字符串具有方法和属性,并且可以肯定地修改String的原型。他们肯定像对象一样。
卡梅隆·马丁

9
@ddlshack:正如Christoph所说,这是由于自动装箱。如果你是通过定义一个字符串var foo = new String('foo');,那么将是一个字符串对象(而且typeof会证实这一点)。但是,如果您通过字符串文字声明它,那么它们就是字符串基元。请参阅:developer.mozilla.org/en/JavaScript/Reference/Global_Objects/...
冒犯君主

26

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 Foundation的页面“ JavaScript的重新介绍(JS教程)”将JavaScript对象描述为“名称/值对的简单集合。因此,它们类似于...”,然后是字典列表,哈希值,来自各种编程语言的哈希表和哈希映射。同一页将对象属性引用描述为哈希表查找。因此对象就像“哈希表”一样。这不会使其他有用的信息无效,但是Chris Lloyd的原始特征并不准确。
C Perkins

2

除了您所说的“哈希”实际上只是对象的简写语法外,您或多或少是正确的。

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


那么,为什么要对对象使用双重标准?
克里斯·劳埃德

它不是双重标准。在第一个示例中,a和b仍然引用同一对象,您只是在修改该对象的属性。在第二个示例中,您将a指向另一个对象。
凯文,

1
不,区别在于在第二种情况下,您正在处理字符串,而不是对象。
Nosredna

1
需要说明的是:这与返回自己的副本的字符串无关。两个代码段不同的原因在Kevin的第二段中(在Squeegy的答案中有更充分的解释)。
Chuck

变量中是否包含字符串或对象都没有关系。您分配一个新的不同值,然后变量包含该新的不同值。
某事

2

这是我的答案版本:

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

0

您将a设置为指向新的字符串对象,而b则始终指向旧的字符串对象。


0

在第一种情况下,您将更改变量中包含的对象的某些属性,在第二种情况下,将新值分配给变量。那是根本不同的事情。变量ab没有以某种方式通过第一个分配神奇地链接在一起,它们只是包含相同的对象。在第二个示例中也是如此,直到您为b变量分配了新值。


0

简单类型和对象之间的区别。

任何对象(如数组或函数)都通过引用传递。

任何简单类型的内容(例如字符串或数字)都将被复制。

我总是有一个方便的copyArray函数,因此可以确保我没有为同一数组创建一堆别名。


在许多情况下,这种区别并不明显,但是Javascript实际上并没有通过引用传递或分配。它复制参考值。
Juan Pablo Califano

这些家伙已经做了很好的解释,因此,我将粘贴链接:stackoverflow.com/questions/40480/is-java-pass-by-reference(我指的是Java,但是传递和分配的语义值/引用与Javascript中的相同)
Juan Pablo Califano

1
实际上,这个答案是不正确的,所有内容都通过JavaScript中的值进行传递。在MDN中,“函数调用的参数是函数的参数。参数通过值传递给函数。如果函数更改了参数的值,则此更改不会反映在全局或调用函数中。但是,对象引用是值也很特殊:如果函数更改了引用对象的属性,则该更改在函数外部可见。” developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
bittersweetryan

基元的行为就像不可变的对象(严格模式下完全一样)。这个答案是不正确的。
Ry-
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.