特别是在V8/Node.js中,当您将primitive type(字符串、数字、布尔值)推入数组时,它是将字符串clone,还是存储引用?

我知道你不能在这样做的同时改变字符串:

let array = []
let x = 'foo'
array.push(x)
x = 'bar'
console.log(array) //=> ['foo']

但是,如果我这样做,是否会多次复制字符串(从而增加内存占用)?

let array = []
let x = 'foo'
array.push(x)
array.push(x)
array.push(x)
...

同样的问题也适用于对象键,如果我这样做,它会克隆字符串吗?

let object = {}
let x = 'foo'
object.a = x
object.b = x
object.c = x

我四处寻找了一下,但没有找到这个问题的直接答案.

Do objects pushed into an array in javascript deep or shallow copy?

This blog post表示:

对象和数组作为指向原始对象的指针推送.Built-in primitive types like numbers or booleans are pushed as a copy.

但我不确定这是否正确(它没有备份).我必须运行一系列彻底的测试,才能真正判断并查看当我推入数组时内存是否增加.我不太确定实现这一点的最简单方法,所以也许V8工程师或其他精通编译器理论的人知道这是如何实现的.

我想用Buffer.byteLength(text, 'utf8')来计算添加到trie中的每个字符串的大小,然后跟踪trie的大致大小(将其中使用的字符串大小相加,并粗略估计用于存储n个对象属性和x长度数组的字节).所以第一步是理解,will my string get copied when I push it into multiple places, or will the same reference be carried around in each place?

我希望这个博客是不正确的,它推送一个引用,只是一旦它被发送到另一个函数,你就不能修改variable.但字符串仍然是一个引用,直到您try 更改变量,就像这样.

推荐答案

(这里是V8开发人员.)

当将字符串存储在数组中(或者实际上是任意位置)时,不会复制该字符串.布尔人也不是.

对于数字,这取决于:它们通常也作为引用存储,除了certain optimized cases,那里有更有效的替代方案.

The reason is twofold:
(1) There is no need to clone strings.
(2) It is simpler and faster not to clone strings.

就实现细节而言,您引用的代码片段显然是错误的.有人可能会争辩说,就可观察到的语义而言,这并不是完全错误的:您的程序的行为无法判断是否存储了字符串的副本,或者只是对它的另一个引用.(当然,这只会使整个语句变得没有意义:如果对象存储为引用,而对于原语我们无法区分,为什么不简单地假设所有内容都存储为引用?)

As a rule of thumb: VMs for dynamic languages like JavaScript treat everything as a reference, except for whichever special cases they choose to optimize (typically some definition of number; search for the terms "smi-tagging" and "nan-boxing" if you want to dig deeper).
Whether a value is a "primitive" or not only affects whether it has object identity:

{foo: 42} === {foo: 42}  // false, objects have identity
42 === 42                // true, numbers have no identity
"foo" === "foo"          // true, strings have no identity

作为基元不会影响值在数组/对象/变量/其他任何地方的存储方式,也不会影响它的分配位置(我有时看到的一个相关错误说法是"基元在堆栈上分配"--不,它们不是).


Added clarification on @Bergi's request:
Of course, when you repeatedly call array.push(x), the size of the array's backing stores grows, because it needs to store increasingly many references to the string. So while the string won't be copied, overall memory usage will increase (on average by one pointer per push, but actually happening in chunks).

Javascript相关问答推荐

使用expressjs加载Nuxt版本=3.9.0(自定义服务器)

Vue.js使用特定索引值的数据更新html

在JavaScript中检索一些文本

RxJS setTimeout操作符等效

如何在使用fast-xml-parser构建ML时包括属性值?

react/redux中的formData在expressjs中返回未定义的req.params.id

我应该绑定不影响状态的函数吗?'

如何找出摆线表面上y与x相交的地方?

将2D数组转换为图形

引用在HTMLAttributes<;HTMLDivElement>;中不可用

使用POST请求时,Req.Body为空

我怎么在JS里连续加2个骰子的和呢?

如何将未排序的元素追加到数组的末尾?

如何确保预订系统跨不同时区的日期时间处理一致?

是否可以将Select()和Sample()与Mongoose结合使用?

如何用javascript更改元素的宽度和高度?

Socket.IO在刷新页面时执行函数两次

如果NetSuite中为空,则限制筛选

使用CEPRESS截取时,cy.Wait()在等待5000ms的第一个路由请求时超时

如何在FiRestore中的事务中使用getCountFromServer