程序执行过程中的变量是需要保存起来供代码调用和计算的,这就用到了内存空间,分为堆和栈
堆(heap)堆内存的简称,动态分配内存,内存大小不一,也不会自动释放。
混沌无序,方便存储和开辟内存空间
堆空间大,由用户控制释放。通过引用计数来控制生命期,回收器来释放最终的堆空间
栈(stack)栈内存的简称,自动分配相对固定大小的内存空间,并由系统自动释放。
线性结构,后进先出,便于管理
堆栈与变量的存储关系
5种基本类型:Undefined、Null、Boolean、Number和String
基本类型都是直接按值存储在栈中的,每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说,更加容易管理内存空间。
引用类型 : 如对象(Object)、数组(Array)、函数(Function) …
引用类型的数据存储于堆中,但是数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。
![](https://img.haomeiwen.com/i7509922/08cb3c3ccab74788.png)
深拷贝、浅拷贝
基本类型拷贝的时候只是在内存中又开辟了新的空间,和原始变量相互独立,不存在深浅,因此深浅拷贝是相对于引用类型来说的
浅拷贝 :引用类型在拷贝过程中,只是拷贝了存在栈内存中的指针,二者同时指向堆内存中相同数据
深拷贝 :引用类型在拷贝过程中,需要将堆内存中的数据全部拷贝,分配新的存储空间和指针,以保证二者完全独立
深拷贝的常用方法:
最简单的通过JSON转换,适用于没有函数的对象
function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj));
}
或者遍历对象中的所有属性和方法,一直找到最下层的基本类型为止,全部拷贝
借用大佬的代码
//这里为了阅读方便,只深拷贝对象,关于数组的判断参照上面的例子
function deepClone(data){
var obj = {};
var originQueue = [data];
var copyQueue = [obj];
//以下两个队列用来保存复制过程中访问过的对象,以此来避免对象环的问题(对象的某个属性值是对象本身)
var visitQueue = [];
var copyVisitQueue = [];
while(originQueue.length > 0){
var _data = originQueue.shift();
var _obj = copyQueue.shift();
visitQueue.push(_data);
copyVisitQueue.push(_obj);
for(var key in _data){
var _value = _data[key]
if(typeof _value !== 'object'){
_obj[key] = _value;
} else {
//使用indexOf可以发现数组中是否存在相同的对象(实现indexOf的难点就在于对象比较)
var index = visitQueue.indexOf(_value);
if(index >= 0){
// 出现环的情况不需要再取出遍历
_obj[key] = copyVisitQueue[index];
} else {
originQueue.push(_value);
_obj[key] = {};
copyQueue.push(_obj[key]);
}
}
}
}
return obj;
}
堆栈的大小问题
堆栈是在内存中的空间,大小就会有显示限制,浏览器会有自动垃圾回收机制,但代码的优化也很重要
每次执行代码时,都会分配一定尺寸的栈空间(Windows系统中为1M),每次方法调用时都会在栈里储存一定信息(如参数、局部变量、返回值等等),这些信息再少也会占用一定空间,成千上万个此类空间累积起来,自然就超过线程的栈空间了。
尤其发生在大数据量的递归处理时,后续介绍处理办法。
网友评论