[英]Primitives and memory allocation in JavaScript
嗨stackoverflow人:)
我是一名 web 开发人员,深入了解一些 JavaScript 概念。
我正在阅读 ECMA 文档和 MDN,以真正了解词法环境、执行上下文、闭包和 memory 管理,但我真的陷入了这个话题。
前任:
const x = 1;
let y = x;
y = 2;
据我了解, 1
和2
是不可变的原语,每次我重新分配变量时,我都会在幕后更改引用。 因此,在第 2 行中, y
和x
指向相同的值。
我的头疼来了:那个值在哪里? 我是否只是将两个对数字1
的引用推入堆栈(在这种情况下)?
我不知道每个原语是否对于程序的所有执行都具有相同的内存地址,或者这是其背后的行为。
有这么多的信息,我不知道我可以信任谁,因为在很多情况下,信息是矛盾的哈哈。
谢谢你们的时间,老实说我也在学习英语所以我希望你们能理解我:)
Javascript 中有基本类型(如数字、boolean、字符串...)和复杂类型(对象、数组)。 所以他们的行为会有所不同:
const x = 1;
<- 将原始值 1 分配给变量 x(在 memory 堆中的某处)
let y = x;
<- 将变量 1 的值分配给变量 y(在 memory 堆中的某处)。 在这里,您将拥有 2 块不同的 memory,每块用于其中一个变量。
y = 2;
<- 将第二个变量的值更改为 2。
当您谈论 memory 地址和指针时,您主要是在谈论复杂类型。 在这里,当您定义 object 时,object 存储在堆中,如果您将 object 分配给另一个变量,那么您将指向相同的值。 一个例子:
const x = {a:1}
<- 创建 object 并分配对 x 的引用
const y = x;
<- 为 y 分配相同的引用
ya = 2
<- 更改 object 的值。
console.log(xa)
<- 将返回 2。
有关更多信息,请查看此网站
Console
进行查找Console
在大多数浏览器中,按F12
会打开Console
。 如果这不起作用,则找到名为Developer tools
的菜单或子菜单并单击它。
Console
选项卡确保选择了名为Console
的选项卡。 点击它。
然后在Console
中键入您要测试的语句。
例如:
const x = 1;
let y = x;
y = 2;
然后键入以下内容以显示您的测试结果 - 您想知道的内容:
[x,y]
或者:
console.log(x,y)
结果表明x
和y
没有引用相同的数字。
这是键入[x,y]
的 output :
Array [ 1, 2 ]
这是输入console.log(x,y)
的 output :
1 2
结果表明分配给y
不会影响x
。
下次您对它的工作原理有疑问时,请做类似的事情。
JS 中原始值的全部处理是它们不是引用的。
在实际实现中,JS 编号可以由一些将 JS 值标识为数字的标记与保存实际数值的64 位一起表示。
在最合理的实现中,示例中的x
和y
在任何时候都不会存储任何引用。 它们只是两个局部变量,可以存储在声明它们的 function 的调用记录中(这有点过于简单化;如果不是这种情况,请参见下文) 。 除非在某些(转义)闭包中使用它们,否则它们可以被认为是存储在寄存器或周围 function 的堆栈帧中的纯局部变量。 当您分配y = x
时,先前存储在x
中的值将按原样复制到y
中。 不创建参考。 x
和y
之间没有建立联系:它们都拥有自己的原始数字值的副本。
您的提法“将两个对数字 1 的引用推入堆栈”似乎比其他提法更接近事实,但似乎仍然不太正确:
JS 的语义不是用堆栈机来描述的。 实现可能不会将单个值推送到任何此类堆栈。 相反,可能更容易将其视为推动包含变量x
和y
的 function 的堆栈帧,然后修改这些变量的值,然后等到堆栈帧从堆栈中弹出,并且其所有内容都被遗忘/ 被覆盖(任何地方都不涉及堆垃圾收集)。
在这个堆栈帧(想象的或真实的)中,两个变量将分别保存描述数字1
的原始值的单独副本。 堆上没有共享值,也没有指向它的引用。
尽管考虑调用记录或堆栈帧可能会有所帮助,但聪明的优化可能会决定将变量x
和y
存储在其他地方,例如,它们可能会确定x
和y
都是严格的 64 位数字,并将它们直接存储在一些寄存器。
(关于关闭的说明)
不过,在某些闭包中使用x
或y
的情况可能会更有趣。 考虑这个例子:
function f() {
var x = 0;
function g() {
return x;
}
function s(a) {
x = a;
}
return {
getX: g,
setX: s,
};
};
var { getX, setX } = f();
setX(42);
console.log(getX());
这里,变量x
被g
和s
的闭包捕获。 由于g
和s
都在返回值中转义,并且在f
终止后仍然存在,因此捕获的x
也必须在f
的生命周期之后保留在 memory 中。 实际上,在这种情况下,捕获的变量x
最终会在堆分配的 object 中,由闭包g
和s
引用。 对应于这个堆分配的 object 的捕获变量x
的属性仍将保留一个原始数字(此处仍然没有引用)。
用于变量的标识符有一个存储其值的位置,使它们成为“左值”,因为它们可以出现在赋值表达式的左侧。
因为 JavaScript 被解释,标识符的名称及其存储位置的记录需要由 JavaScript 引擎维护。 在标准中,该记录被称为名称的“绑定”,并保存在环境记录中。 获取变量的值然后查找其名称的绑定以获取其值。 (标准中GetValue操作的第 6 步)
所以在例子中
const x = 1;
let y = x;
y = 2;
x
和y
在环境记录中的位置不同。
现在 JavaScript 是松散类型的,这意味着变量可以保存 JavaScript 支持的任何数据类型的值。 暗示,一个值的表示必须包含关于它的类型以及它的实际值的信息。
这是人们开始猜测或假设(原始)值如何由 JavaScript 引擎在内部保存的点。 您可能遇到过 JavaScript 中的“所有内容”之类的语句被实现为内部(本机代码) object 并作为指针传递。 这个假设出现在问题中:
因此,在第 2 行中,y 和 x 指向相同的值。
这不一定适用于所有 JavaScript 引擎。 JavaScript 程序员无法控制引擎如何实际实现原始值,并且假设“指向内部对象的指针”model 可能不正确。 我知道的另一种选择涉及使用标记的NaN 值来传达非数字信息:使用 16,777,214 个不同的 32 位 NaN 值(更多用于 64 位浮点),有足够的单独值可用于未定义、boolean 和 null 值等. 这种方法有可能避免将数据类型和值作为内部 object 的单独属性来保存。
关于
对于程序的所有执行,每个原语是否都具有相同的内存地址?
这将取决于 JavaScript 引擎的值的效率、优化和实现。 有趣的是,一些引擎会尝试合并字符串原语,而不是创建同一字符串值的多个多个副本,但他们在这方面做得如何是另一个问题。 其他类型的原语是否有地址可以追溯到内部实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.