JavaScript中的原始类型和引用类型

12

我一直以为JavaScript中有原始类型和引用类型。在日常工作中,我从未受到过这方面的影响,但最近开始接触更多的JS并希望更新我的“思考”。换句话说,我本来会打赌$20以下代码会返回68:

var my_obj = {};
var tmp_obj = {};

tmp_obj.my_int = 38;
my_obj.tmp_val = tmp_obj.my_int;
tmp_obj.my_int = 68;

alert('68 means reference, 38 means primitve: ' + my_obj.tmp_val);

但它返回 38。

图片描述

即使一个数字存在于引用类型的上下文中,它们所有实例是否都是原始类型?如果是,我感到非常惊讶,并且发现这种行为很奇怪(那我就会少 20 美元)。或者我的示例并没有展示我认为的内容?

提前感谢。

更新 #1

哇,感谢所有的回答。这里有一个稍微更改过的示例,它帮助我更好地理解:

var my_obj={};
var tmp_obj={};
var my_obj_2=tmp_obj;
tmp_obj.my_int=38;
my_obj.tmp_val=tmp_obj.my_int;
tmp_obj.my_int=68
alert('68 means reference, 38 means primitve: ' + my_obj.tmp_val);   // 38
alert('68 means reference, 38 means primitve: ' + my_obj_2.my_int);  // 68
my_obj_2.my_int=78;
alert(tmp_obj.my_int); // tmp_obj is now 78 ie two way

7
my_objtmp_obj 是两个独立的对象。更改其中一个对象属性的值不会影响另一个对象,无论原始值来自何处。对于 var a = 1; var b = a; a = 2; ... b 仍然是 1 - Felix Kling
与之比较:tmp_obj = my_obj; tmp_obj.my_int = 68; alert(my_obj.my_int) - bfavaretto
4
无论属性是基本类型还是引用类型,都不受影响。 - Matt Ball
5个回答

7
您提供的示例代码只有一个问题,如果您有 标签包含的内容,它将不起作用。
     my_obj = tmp_obj;

那么,所有的属性都会指向同一个引用,因为只有一个对象。

但是当你写下以下代码:

     my_obj.tmp_val = tmp_obj.my_int;

那么my_obj.tmp_val将取存储在tmp_obj.my_int中的值,但这不会在2个对象之间创建新的引用。


这基本上是我没有正确理解的内容;非常感谢! - timpone

6

如果我可以使用类似Java的语法,那么看起来你期望代码表现为这样,其中有一个整数对象其值被改变。

tmp_obj.my_int = new Integer(38);
my_obj.tmp_val = tmp_obj.my_int;
tmp_obj.my_int.setValue(68);

由于它没有打印出68,你得出整数必须是原始类型而不是引用类型的结论。然而,这并不一定是正确的。考虑以下替代解释:
tmp_obj.my_int = new Integer(38);
my_obj.tmp_val = tmp_obj.my_int;
tmp_obj.my_int = new Integer(68);

这里的整数是引用类型,但 my_obj.tmp_val 仍将包含值 38 ,因为将整数分配给变量等于覆盖引用。你可以认为整数是不可变对象。JavaScript 在呈现统一的一切都是对象的视图方面做得非常好,所以这比“整数是原始类型”更好的解释结果。

1
回答关于一种语言的问题,却用另一种语言的例子来解释,这有点有趣,但你把它讲得很到位。不错的答案+1。(由于在JS中无法改变Number对象的值,因此真的没有好的方法来演示这一点)。 - Felix Kling
在指出赋值操作符只能分配引用的参考对象而不能分配引用的被引用对象时加1,比如在Javascript、Python或Java中(但与C++相反,在这种情况下指针可以在赋值之前解除引用,例如*p=3;),人们无法确定原始类型是“值类型”还是“引用类型”;只有对于复合类型才能确定。 - Géry Ogam

4

是的,像数字和字符串这样的值就像原始值一样工作。它们是不可变的。将新值分配给变量会替换旧值,而不是更改已有的值。

例如:

var x = 42;
var y = x; // y == 42
x = 1337; // this puts a new value in x, it doesn't change 42 to be 1337
alert(y); // it's still 42

同样适用于字符串:
var x = "42";
var y = x; // y == "42"
x = "1337"; // this puts a new string in x, it doesn't change "42" to be "1337"
alert(y); // it's still "42"

此外,如果您使用对象属性;
var o = {};
o.x = 42;
o.y = o.x; // o.y == 42
o.x = 1337; // this puts a new value in o.x, it doesn't change 42 to be 1337
alert(o.y); // it's still 42

一个值的行为取决于它的类型,而不是它是存储在常规变量中还是存储在对象的属性中。
即使字符串在内部被实现为对象,它也是不可变的,并且像一个值一样工作。复制字符串可能会复制对象的引用,但其效果是你得到一个单独的副本,因为没有任何东西可以改变字符串的值本身。

3
我一直认为JavaScript中有原始类型和引用类型。
是的,但唯一的引用值是对象;原始值不可变。
即使数字存在于引用类型的上下文中,它们所有的实例也都是原始类型。
是的,它们的上下文并不相关。

数组也是引用。 - Barmar
3
数组也是对象。 - Bergi

2
我相信这里有两个关键点需要理解:
  1. 原始值永远不会是引用
  2. JavaScript的引用不能被赋值,只能复制。
考虑以下代码以证明第一个观点:
var a = 10,
    b = a,
    c = {foo : 20}
    d = c.foo;

b = 20;
d = 30;
a;     // 10;
c.foo; // 20;

所以,你有“槽”(变量或属性)保存原始值,而不是引用。更改其中一个槽中的值不会影响其他槽。
考虑第二点:
var a = {foo: true},
    b = a;
b.foo; // true
b.foo = false;
a.foo; // false;
b = {bar: false};
a;     // {foo: false}

变量 a 包含一个对象,而 b 最初是指向同一个对象的引用。将值赋给 b.foo 会影响到 a,因为 ba 指向完全相同的对象。然而,这些引用就像存储在给定插槽中的任何其他值一样:当您将其他内容分配给插槽时,引用只会被替换为不同的值。因此,对 b 的赋值不会影响 a

非常好的观点,我不得不读两遍才能理解其中的微妙之处 - 因此赋值可以创建一个混合对象,其中有一部分是引用的一部分是独立的。 - timpone
不,我认为我的解释很混乱,我会重新措辞。如果您在给b分配其他值之前尝试更改b.foo,它将影响a。但是在b = {bar: false};之后,这两个对象之间就没有链接了。也没有混合对象。 - bfavaretto
好的,类似于 UNIX 进程模型中的写时复制。 - timpone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接