JS中对象 “值” 的修改,是在堆中同一个内存地址上修改吗?

基本数据类型

    var a = 1; // 假设栈中开辟内存空间,地址为0x0202,存放值为1,a指向0x0202这个地址
    a = 2; //  栈中开辟内存空间,地址0x0203,存放值为2,a指向0x0203这个地址,0x0202没有被指向

对于基本数据类型(number、string、boolean、undefined、null)其值是“不可变”的,每次值被修改,都会在栈中开辟新的内存空间,写入修改值后,改变标识符的指向。

那么,对于堆中的变量也符合这种规则吗?

引用数据类型

    var obj = {a:1}; // 假设堆中开辟内存空间,地址为0x1020,其值为1,obj.a指向0x1020这个地址
    obj.a = 2; //  请问:是直接在堆中地址为0x1020内存上重写值为 2?
    //  还是像上面一样开辟新的内存空间,地址为0x1030,存放值为2,修改obj.a的指向为 0x1030?

请问:是直接在堆中地址为0x1020内存上重写值为 2?还是像上面一样开辟新的内存空间,地址为0x1030(假设),存放值为2,修改obj.a的指向为 0x1030?

阅读 3.2k
2 个回答
对于基本数据类型(number、string、boolean、undefined、null)其值是“不可变”的,每次值被修改,都会在栈中开辟新的内存空间,写入修改值后,改变标识符的指向。

这是你说的吧?

基本数据类型是指内存中存放的数据的类型是基本类型,而不是内存地址。但是“每次值被修改都会开辟新的内存空间写入,再改变标识符指向”这件事和数据类型无关,是更底层的逻辑,而且这个逻辑甚至和JS无关。

你想想看,var a = {},然后 a.t = 5,此时a的值变了吗?或者说a的标识符指向的地址中存储的内容变了吗?当然没变,所以我们必须用var a = {}; a = {t: 5}的方式才能让侦听发现a变了。同理,任何语言中只要支持“引用”的,是不是都是一样的结果?

这样总结吧,值是存放到某个内存地址,标记使用,然后改变指向标识,再将原始地址标记为未使用。但是通常开发者并不需要关注这个事情。

至于为什么这样搞,就是@xdsnet 说的那样了,原始地址放不开新数据怎么办?把别人都挪一下地方?那不累死了。
注意细节:var a = 1; // 假设栈中开辟内存空间,地址为0x0202,存放值为1,a指向0x0202这个地址 这个描述是不完整的,因为1是整型,所以需要先开辟一个4字节空间,再把1放进去,然后a指向0x0202,l=4。所以当a = 1.5时,需要先开辟一个8字节空间才行。这个空间必须是连续的。

说实话我不是特别苟同这里:

 var a = 1; // 假设栈中开辟内存空间,地址为0x0202,存放值为1,a指向0x0202这个地址
    a = 2; //  栈中开辟内存空间,地址0x0203,存放值为2,a指向0x0203这个地址,0x0202没有被指向

这里的a就是地址,对a重新赋值,就是将对应地址的内容进行变更。为什么会有新的地址出现。不是很确定这段内容的出处。
基础类型的值确实在栈上,obj.a一个意思就是个地址,对obj.a重新赋一个基础类型的值,还是改变同一个地址的内容。
地址是地址,内容是内容。obj.a指向0x1020这个地址 这个描述可以参考一下有指针的语言,比如c语言里,&和*的含义和使用。
粗俗的说,0202变0203,然后0202没人用,没人用就得回收,回收完还得调整内存空间,那直接用0202不就好了吗。

====

我还是比较坚持我的观点:
image.png
a初始化,和a重新赋值,都是Star0,木有新地址,木有新空间。
image.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题