重新分配、变异、引用类型和值类型

Reassignment, mutation, reference types, and value types

您如何恰当地解释为什么这两个示例不同?

// Reassignment
let a = 1;
let b = a;
a = 2;
console.log(b); // → 1

// Mutation
let myArray = [1, 2, 3];
let ourArray = myArray;
ourArray[2] = 10;
console.log(myArray); // → [1, 2, 10]

关于为什么变量标签在两者之间表现不同,许多资源声称这是因为引用类型和值类型之间的差异。但是,这不只适用于 本身之间的差异吗?

当明确谈论变量标签时(即为什么第一个例子中的标签不跟踪值的变化,而第二个例子中的标签),真正的区别不是与 突变与重新分配?

毕竟,我们可以很容易地重新分配 myArray 或 ourArray,并且它们都不会有任何正在进行的 link。而且,如果在 JavaScript 中原始值在理论上是可变的,则可以使第一个示例的行为与第二个示例相同。

跟进问题

我还阅读了有关原始值是否仅在内存中存在一次的相互矛盾的信息。例如,如果第一在内存中只存在一次,那么将变量标签可视化为名称标签是合适的,每个引用第一的名称标签都是 'stuck' 到同一个第一,对吗?

同样,如果第一名能够发生变异,那么每个名字标签都会追踪到第一名的相同变化。但由于 one 是不可变的,nametags 可以继续引用相同的数字 1,也可以将它们取下并固定为不同的值(重新分配)。

我走的路对吗?

您在问题中将实施细节 ("exist once in memory") 与评估策略 ("reference types and value types") 和特定表达式 ("mutation vs. reassignment") 混合在一起。实施细节因浏览器而异,将来可能会发生变化。

Javascript中有两种评价策略:

  • Copy by value(调用函数时按值传递)
  • Copy by reference(调用函数时按引用传递)

值类型

值类型总是按值复制。假定字符串在 Javascript:

中是可变的
let s = "abc";
let t = s;

s[0] = "x";
s; // "xbc";
t; // "abc";

基于值类型的变量(或标识符或名称绑定)直接包含它们的值。如果这样一个可变字符串发生变异,那么只有相应标识符的值会受到影响。

与字符串不同,数字是原子的。对于原子原语,突变和重新分配没有实际区别:

let n = 0;
n = 1, n++, n = n + 1; // all reassignments

引用类型

持有引用类型的变量只包含对该类型的引用,它是Javascript中的一个复杂值(对象)。如果两个变量引用同一个对象,则它们各自保存该引用的副本,而不是直接别名。因此,如果两个变量之一被重新分配,这不会影响另一个。

引用类型具有身份,也就是说,它们的身份不再与值相关联:

let xs = [1];
xs === [1]; // false

When explicitly talking about just the variable label, doesn't the true difference have to do with mutation vs. reassignment?

当然可以。我假设你的意思是一个 mutates 对象和 assigns 变量。但是你不能总是那样区分它们。我也可以说我分配给对象属性(在你的第二个例子中)或变异 "variable-labelled" 内存单元(在你的第一个例子中)。真的有区别吗?

但实际上,变量(标签、存储单元、寄存器)总是以相同的方式工作,无论它持有原始值还是参考值。

I keep reading conflicting information on whether or not primitive values only exist once in memory.

嗯,重点是没关系,因为原始值(根据定义)是不可变的。一个 JS 实现可以复制它们,或者共享引用,脚本无法区分这些行为。

在现实世界中,数字和布尔值通常被复制,而字符串通常 interned(共享)。