另起炉灶 or 顺藤摸瓜

两个对象的关系,最模棱两可的疑惑往往源于搞不清它们之间是另起炉灶还是顺藤摸瓜
a = b
同样是赋值操作,
b 是对象,拷贝的就是地址。对新地址而言 a 可以顺腾摸瓜,对旧地址而言 a 是另起炉灶
b 是基本类型,拷贝的是值。无论如何 a 都是另起炉灶

JS 的栈空间和堆空间

与其他编程语言类似,JS的内存空间同样可分为栈空间和堆空间,JS中那些具有固定大小的基本数据类型(Null、Undefined、Number、String、Boolean、Symbol)存储在栈空间中,而对象都分布在堆内存空间中,在栈空间中存储的是存储于堆空间的对象的引用地址:

这就意味着,你在赋值一个变量的时候

  • 如果变量是对象,你改变的是地址
  • 如果变量是基本类型,你改变的是值

特别地,数组也是对象

正文

上面是我后来加的内容,以下转本人知乎原文

在你决定是否看这篇文章之前,试做以下四道小测。做对了,说明你对js内存的问题有很清晰的理解,没必要往下看;如果做错了,那么恭喜你,这篇文章将给你一个提升自己的机会。

问题 1:

问题 2:

问题 3:

问题 4:

回答完毕?请按 Ctrl + End 到本文最底部查看答案。
做对的同学,great. 请按Ctrl + W关闭本页,因为你没有必要浪费时间看完后边内容。
做错的同学,congratulation. 因为接下来,你将学到一个解决以上“难题”的技巧。


一. 预备知识1:js存储数据的方式

1.js里的数据类型一共7种,分别是:

Number, String, Boolean, Symbol, undefined, null 和  Object

前6种是简单类型,最后一种Object是复杂类型。这两种类型的区别在于内存存储它们的方式。

2.内存分为栈内存(Stack)跟堆内存(Heap)两部分,js引擎在解析代码时:

  • 如果遇到简单类型的声明,就在栈内存里开辟一个空间,将简单类型的值放进去;
  • 如果遇到复杂类型,就在栈内存,堆内存分别开辟一个空间。将自身的值放进堆内存,并将存在堆内存的这块空间的地址,放进栈内存。

3.赋值语句在内存中的操作规则

发生在代码里的赋值语句(如b = a),在内存中对应操作为:将a的栈内存区的内容,抄到b的栈内存上。而堆内存区,不作改动。

二. 预备知识2:js内存图

分析问题时,可以通过以下步骤,画一张内存图:

step1 草图一分为三,左代码,中Stack,右Heap

step2 每逢声明语句,普通类型值写入Stack

step3 每逢声明语句,对象类型地址写入Stack,值写入Heap,并用箭头连接两者

step4 每逢赋值语句 → 将Stack区的内容照抄

类似开头所有内存问题的解答,都有一个强大的方法,就是“画内存图”,下面将展示内存图在解决以上问题的应用。

三. 问题分析与解决

解答1:

解答2:

解答3:

解答4:

四. 扩展知识1:浅拷贝与深拷贝

b 变 a 也变就是深拷贝,b 变 a 不变就是浅拷贝
由此可见,所有简单类型的拷贝,都是深拷贝,只在对象拷贝的时候才有区分。

五. 扩展知识2:什么是垃圾回收?

1.什么是垃圾?

a:Heap区的一个对象,如果没有被引用,它就是垃圾,将Heap区的这部分内存释放,就叫“垃圾回收”。

2.关于垃圾回收的一个小问题

Q:

a:

由图可见,Heap区的function仍然有指向它的箭头,故function不是垃圾,它仍是被人引用、被人需要的。


以下是开头四个问题的答案,你对了吗?

问题1:1

问题2:'b'

问题3:'a'

问题4:{name: "a"}