繁体   English   中英

JavaScript 中的原语和 memory 分配

[英]Primitives and memory allocation in JavaScript

嗨stackoverflow人:)

我是一名 web 开发人员,深入了解一些 JavaScript 概念。

我正在阅读 ECMA 文档和 MDN,以真正了解词法环境、执行上下文、闭包和 memory 管理,但我真的陷入了这个话题。

前任:

const x = 1;

let y = x;

y = 2;

据我了解, 12是不可变的原语,每次我重新分配变量时,我都会在幕后更改引用。 因此,在第 2 行中, yx指向相同的值。

我的头疼来了:那个值在哪里? 我是否只是将两个对数字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的菜单或子菜单并单击它。

Select Console选项卡

确保选择了名为Console的选项卡。 点击它。

创建你的测试

然后在Console中键入您要测试的语句。
例如:

const x = 1;
let y = x;
y = 2;

显示结果

然后键入以下内容以显示您的测试结果 - 您想知道的内容:

[x,y]

或者:

console.log(x,y)

结果表明xy没有引用相同的数字。

这是键入[x,y]的 output :

Array [ 1, 2 ]

这是输入console.log(x,y)的 output :

1 2

结果表明分配给y不会影响x

冲洗并重复

下次您对它的工作原理有疑问时,请做类似的事情。

JS 中原始值的全部处理是它们不是引用的。

在实际实现中,JS 编号可以由一些将 JS 值标识为数字的标记与保存实际数值的64 位一起表示。

在最合理的实现中,示例中的xy在任何时候都不会存储任何引用。 它们只是两个局部变量,可以存储在声明它们的 function 的调用记录中(这有点过于简单化;如果不是这种情况,请参见下文) 除非在某些(转义)闭包中使用它们,否则它们可以被认为是存储在寄存器或周围 function 的堆栈帧中的纯局部变量。 当您分配y = x时,先前存储在x中的值将按原样复制到y中。 不创建参考。 xy之间没有建立联系:它们都拥有自己的原始数字值的副本。

您的提法“将两个对数字 1 的引用推入堆栈”似乎比其他提法更接近事实,但似乎仍然不太正确:

  • JS 的语义不是用堆栈机来描述的。 实现可能不会将单个值推送到任何此类堆栈。 相反,可能更容易将其视为推动包含变量xy的 function 的堆栈帧,然后修改这些变量的值,然后等到堆栈帧从堆栈中弹出,并且其所有内容都被遗忘/ 被覆盖(任何地方都不涉及堆垃圾收集)。

  • 在这个堆栈帧(想象的或真实的)中,两个变量将分别保存描述数字1的原始值的单独副本。 堆上没有共享值,也没有指向它的引用。

  • 尽管考虑调用记录或堆栈帧可能会有所帮助,但聪明的优化可能会决定将变量xy存储在其他地方,例如,它们可能会确定xy都是严格的 64 位数字,并将它们直接存储在一些寄存器。


(关于关闭的说明)

不过,在某些闭包中使用xy的情况可能会更有趣。 考虑这个例子:

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());

这里,变量xgs的闭包捕获 由于gs都在返回值中转义,并且在f终止后仍然存在,因此捕获的x也必须在f的生命周期之后保留在 memory 中。 实际上,在这种情况下,捕获的变量x最终会在堆分配的 object 中,由闭包gs引用。 对应于这个堆分配的 object 的捕获变量x的属性仍将保留一个原始数字(此处仍然没有引用)。

用于变量的标识符有一个存储其值的位置,使它们成为“左值”,因为它们可以出现在赋值表达式的左侧。

因为 JavaScript 被解释,标识符的名称及其存储位置的记录需要由 JavaScript 引擎维护。 在标准中,该记录被称为名称的“绑定”,并保存在环境记录中。 获取变量的值然后查找其名称的绑定以获取其值。 (标准中GetValue操作的第 6 步)

所以在例子中

const x = 1;

let y = x;

y = 2;

xy在环境记录中的位置不同。

现在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM