js堆和栈
在js引擎中对变量的存储主要有两种位置,堆内存和栈内存。
-
栈内存
- 主要用于存储各种基本类型的变量(boolean, null, undefined, string, number, symbol)
- const, let对象,当我们定义const对象的时候,我们说的常量就是指针,就是const对象对应的堆内存指向是不变的。但是值的大小是可以改变的。所以const,let不能二次定义的原因: 就是用来初始化的时候,会首先遍历当前的内存栈,有的话就返回错误。
-
堆内存
- 主要存储引用类型,如Object
- 堆内存中的对象不会随方法的结束而销毁,就算方法结束了,这个对象也可能被其他引用变量所引用(参数传递)。
-
内存分配和垃圾回收
- 内存分配: 栈内存线性有序存储,容量小,系统分配效率高。而堆内存首先要在堆内存新分配存储区域,之后又要将指针存储到栈内存中,效率相对较低。
- 垃圾回收: 栈内存变量基本上用完就回收了,而堆内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收。
传值与传址
基本类型与引用类型最大的区别实际就是传值与传址的区别。
var a = [1,2,3,4,5];
var b = a;
var c = a[0];
console.log(b); // [1,2,3,4,5]
console.log(c); // [1]
// 改变数值
b[4] = 6; // 能改变a,因为b的地址已经指向了a
c = 8; // 不能改变a,因为只是简单的获取一个数据值,地址没有指向a
console.log(a[4]); // 6
console.log(a[1]); // 1
深浅拷贝
浅拷贝只是将第一层的引用指向了新地址,但是更低层的地址还是指向原来的地址。
var obj = {
a: 1
}
function copy (obj) {
var result = {}
for (var key in obj) {
result[key] = obj[key]
}
return result
}
obj.c = [1];
var newObj = copy(obj)
// 传普通值的时候,不会改变原来的
newObj.b = [1]
console.log(newObj); // {a:1,c:[1], b: [1]}
console.log(obj); // {a: 1, c: [1]}
// 传引用值的时候, 因为他里面的属性还是指向同一个地址,所以原来的对象里面的该引用值也会改变。
newObj.c.push(2);
console.log(newObj); // {a:1, c:[1, 2], b: [1]}
console.log(obj);// {a: 1, c: [1, 2] }
深拷贝
var obj = { a: 1 }
function simpleDeepCopy (obj, newObj) {
for (var key in obj) {
if (obj[key] instanceof Object) {
newObj[key] = obj[key].constructor === Array ? [] : {}
simpleDeepCopy(obj[key], newObj[key])
} else {
newObj[key] = obj[key]
}
}
return newObj
}
obj.c = [1]
obj.d = [{ a: 1 }]
obj.e = { e: 5 }
var newObj = {}
// newObj = deepCopy(obj, newObj)
newObj = simpleDeepCopy(obj, newObj)
newObj.c.push(2)
newObj.d.push({ b: 2 })
newObj.d[0].c = 0
// obj.e.c = 6
console.log(obj) // { a: 1, c: [ 1 ], d: [ { a: 1 } ], e: { e: 5 } }
console.log(newObj) //{ a: 1, c: [ 1, 2 ], d: [ { a: 1, c: 0 }, { b: 2 } ], e: { e: 5 } }