JavaScript的7种数据类型中,number、string、boolean、null、undefined、symbol为基本数据类型,而object为复杂类型(complex type)。在计算机中,简单类型数据存放在栈(Stack)中,复杂类型的数据则存放在堆(Heap)中而其Heap地址存放在Stack里。
五个面试题
- 第一题
var a = 1
var b = a
b = 2
请问 a 显示是几?
当执行到var b=a时,内存中可以表示为(因为a、b是number型数据,所以是基本数据类型,存在stack中):
此时执行b=2直接将 2 的值放在 b 的stack中,如:
所以b的改变并没有影响到a,a的结果还是1。
- 第二题
var a = {name: 'a'}
var b = a
b = {name: 'b'}
请问现在 a.name 是多少?
从上图可知对于复杂类型,在栈(Stack)上存储的是一个地址,在堆(Heap)上存储的是数据。按照地址有指向关系。运行到var b = a时,是将a中的addr1地址复制一份放到b的stack中。当运行b = {name: 'b'}时是在heap中重新开出一块放置{name: 'b'}且地址为2,并且b的地址更改为addr2。这个时候b指向addr1的作废,重新指向2。所以这个时候a还是原来的。即a.name的结果是a。
- 第三题
var a = {name: 'a'}
var b = a
b.name = 'b'
请问现在 a.name 是多少?
当运行至var b = a时a和b的指向相同,此时运行b.name = 'b',更改了heap中地址1中的信息,如图所示。所以这个时候a.name的结果是{name: 'b'}。
- 第四题
var a = {name: 'a'}
var b = a
b = null
请问现在 a 是什么?
当运行至var b = a时a和b的指向相同,这个时候运行b = null(这是一个普通类型,直接将null的值在stack中给向b),所以导致b在heap中的指向消失并没有改变heap内存中数据。所以a的结果还是本身,即{name: 'a'}。
-
第五题
a.x=a={n:2}一句实际上写成“a={n:2};a.x=a”更为直观。第一句var a={n:1}操作在heap里存放“n:1”,stack里存放“a:ADDR78”指向heap这里;2句“var b=a”则在stack里放入“b:ADDR78”;3句把“a(a此时门牌号为78)={n:2}”赋给“a.x(a此时门牌号仍为78)”,根据=运算符从右至左的规定先执行“a={n:2}”——新造n:2放入79号房间并把门牌号79传给a,则stack里a:ADDR79;再执行“a.x(门牌号78)='ADDR79'”,即在78号仓房里新放入了“x:ADDR79”;至此,alert(a.x)去找栈里a映射的79号房里并没有x这么个名的东东,所以输出“undefined”。而alert(b.x)时,Stack里b一直映射Heap的78号地址,里面的x值为ADDR79,alert调用toString()方法将对象ADDR79输出[object Object]。
小结
JS在数据区分为栈内存(Stack)和堆内存(Heap)。普通类型只是在栈内存中运行,而对象的是堆内存中存储数据在栈内存中存储heap地址,同过引用关系。
值类型之间传递的是值,引用类型之间传递的是地址(引用)。
值类型作为函数的参数传递的是值,引用类型作为函数的参数传递的是地址(引用)。
深拷贝和浅拷贝
var a = 1
var b = a
b = 2
上边的代码中当对b改变a完全不受影响,即为深拷贝。
对于简单类型中,赋值就是深拷贝。
对于复杂类型(对象),就有深拷贝和浅拷贝一说。因为复杂类型存在引用关系,所以在更改其中一个对象时,两者的指向还是相同的,即存在一个发生变化,另个也一起变化。
垃圾回收
垃圾回收是指,若一个对象没有被引用,它便是垃圾将被浏览器回收。