JS数据类型转换与内存模型
面试
一张图表示,简单明了~
算法
内存分为RAM和ROM。RAM是随机存取存储器,用户可读可写,计算机断电后,存储在RAM的信息将被删除。RAM具体分为SRAM(Static RAM)和DRAM(Dynamic RAM)。咱们 如今所说的内存通常指的都是DRAM,SRAM速度更快,可是容量相对于其余类型内存而言也会小一些,而且价格也较为昂贵。通常SRAM做为CPU和DRAM之间的缓存(cache)。ROM是只读存储器,ROM的内容是制造商写进去的,用户只能读,但不能写。ROM在切断电源后,数据也不会丢失。计算机会给当前执行的程序动态地分配内存空间,例如用户打开浏览器打开诸多网页,计算机会给浏览器分配内存,浏览器会给每个页面分配内存,会给页面的HTML,CSS,JS分配内存。数组
由于内存中的数据在断电以后会消失,因此为了数据的保存,就须要使用其余类型的存储器。这类存储器通常是磁盘,固态硬盘(ssd)等,也就是所谓的外存。浏览器
JS中的基础数据类型每每都保存在栈内存中,由于这些值都有固定的大小。
例如:var a = 1;
内存分配图大概是这个样子的:
缓存
var a = {'key':'value'};
内存分配图大概是这个样子的:
var a = {n:1};
var b = a;
a.x = a = {n:2};
=============================
问:
console.log(a.x)= ?
console.log(b.x) =?
复制代码
这道题涉及到了连续赋值的状况,在程序运行以后,先肯定好了a.x 和 a的引用,而后再从右往左开始赋值 。本题的内存分析以下:
bash
var a = {n:1};
var b = a;
复制代码
代码执行到这里时,内存图模型以下:
函数
a.x = a = {n:2};
时,JS首先会肯定a在stack中的“地址”也就是指向对象的引用。
a.x = a = {n:2};
// 本例中a在stack中的地址为addr=31,在肯定好这个信息后,代码会从右至左开始执行。
复制代码
首先是a = {n:2}
ui
a.x = a
,在内存模型中,此时左边的a在stack中对应的地址为addr31,而右边的a在stack中的地址已经变为了addr33。因此这个操做至关于在addr31所指向的对象中添加x属性,x的属性值为addr33指向的对象。
参考内容:JavaScript内存管理一文。spa
引用计数算法定义“垃圾”的标准很简单,就是看一个对象是否有指向它的引用,若是没有就回收。可是引用计数算法却存在着许多问题,试想若是在堆内存中,有对象相互引用,即使是它们再也不使用,可是GC不会进行回收,最后就会致使内存泄漏的问题,而IE6就是使用引用计数算法来进行垃圾回收的。
如代码:code
function c() {
var o1 = {};
var o2 = {};
o1.a = o2;
o2.a = o1;
return "IE6'Bug"
}
c();
复制代码
按理来说方法在调用以后,对象o1,o2能够进行回收了,可是在堆内存中二者互相引用,是根据引用计数算法的定义,在堆内存的这两个对象还存在着引用的关系,因此这部份内存不会被回收,接下来就有可能带来内存泄漏的问题。什么是内存泄漏呢?内存泄漏(Memory Leak) 是指在程序中动态被分配的堆内存中,因为某种缘由,致使程序未被释放或者说没法释放而形成了系统内存的浪费,进而致使程序运行速度减慢,甚至是系统崩溃。
标记清除算法是现代浏览器主要使用,并在此之上加以改进并应用的算法,标记清除算法定义“垃圾”为从栈内存开始“没法达到”的对象。对于IE6出现的Bug,即使是堆内存中两个对象互相引用的状况,标记清除算法也能够进行回收。
如:
var a = 1;
var b = a;
复制代码
这个过程就是深拷贝,对于基本类型来讲,赋值的过程就是深拷贝。当咱们尝试去改变b的值时,变量a的值不会发生变化,换句话说,一个变不会影响另外一个这就是深拷贝。浅拷贝则偏偏相反,对于复杂类型而言:
var a = {name:'a'};
var b = a;
复制代码
a和b指向同一个对象,当改变a.name或b.name时,另外的引用指向的对象(其实就是本身)会发生改变,因此咱们能够这么理解:一个变会影响另外一个变这就是深拷贝。对于复杂类型来讲,也能够经过代码实现深拷贝,后续再见~