一、栈内存的创建和销毁
1.1 栈内存的创建
- 当浏览器打开时,首先会开辟形成一个顶层的栈内存,就是全局作用域;
- 函数执行时,也会开辟一个供函数代码执行的栈内存(私有作用域)
var a = 1; // 全局作用域
function f(){
var b = 2; // 私有作用域
return b;
}
栈内存里,每个变量的值之间互不影响
var a = 10;
var b = a;
a++;
console.log(b); //10
//此时改变a的值,b的值不会受影响。
//基本数据类型的变量之间不相互影响
1.2 栈内存销毁
- 全局栈内存:当页面关闭时才会销毁
- 函数的私有作用域:一般函数执行完成后,栈内存自动销毁。但是有一些特殊情况需要注意:
正常情况下,函数执行会形成一个栈内存(作用域),当函数执行完成就会自动销毁。
但是函数执行完成后,当前形成的栈内存中,某些内容被栈内存以外的变量占用了,此时栈内存不能释放(一旦释放外面找不到原有的内容了)。栈内存不销毁,保存在栈内存中的数据也不会被销毁,如代码所示。
// 1.函数返回值被占用
function f1() {
return {
name: "藤原拓海"
}
}
var obj = f1(); // 函数f1执行,将返回值赋值给obj,当里面定义的变量或对象被外部obj变量占用,因而作用域不销毁
f1(); // 正常形成栈内存,但是执行后会被销毁
// 2.函数内部的引用数据类型被外部占用,函数执行的作用域不销毁
var x = null;
function fn() {
x = {
name: '藤原豆腐店'
}
};
fn(); // 此时x占用着fn的作用域中对象{name: 'q1'},fn的作用域不销毁
二、堆内存的创建和销毁
2.1 堆内存的创建
创建一个对象、数组、函数等引用数据类型,或当new一个实例对象后,浏览器都会分配一块堆内存地址,存储引用数据类型的数据;
引用数据类型的创建实际上还是存在栈内存中,只不过基本数据类型存储的是值,而引用数据类型存储的是一个内存地址,它的值是存储在堆内存中
函数每一次执行,都会形成一个全新的堆内存。当有变量对其进行引用或调用时,函数对象就会被压入栈中执行,每次函数执行都是在一个全新的环境里面执行,所以函数每次执行都是互相独立的,如下代码所示
function f() {
console.log('f执行了');
return {
name: '周杰偷'
}
}
var o1 = f(); //调用f函数将返回值赋值
var o2 = f();
console.log(o1); // {name: '周杰偷'}
console.log(o2); // {name: '周杰偷'}
console.log(o1 === o2); //false 因为f函数被调用了两次,每一次调用都会创建一个新的堆内存,且引用类型对比的是内存地址,所以这里o1和o2并不相同,且互不影响
提一下堆内存地址的赋值问题,有如下代码
var obj = new Object();
obj.name = "悟空";
var obj2 = obj;
obj2.name = "八戒";
console.log(obj.name) //八戒
obj = {}; //若把obj的引用地址改变,则obj指向空对象{},相当于“断开连接”
obj = null; //同样是“断开连接”,无论是{}或null,对obj2都没影响,obj2依旧是{name:'八戒'}
var obj2 = obj的结果如下图所示
这里将obj赋值给obj2,实际上就是创建一个变量obj2且将obj的引用地址复制给obj2,存储在栈内存,即obj和obj2指向的是同一个对象(同一个堆内存地址)当它们其中一个改变了对象的属性,另一个也会随之改变;但其中一个变为了空对象或者null,对另一个的堆内存的值没有影响
2.2 堆内存的销毁
将所有引用堆内存的地址置为null即可,没有变量占用这个内存,浏览器空闲时就会释放掉