堆栈内存开辟及销毁、let、const
JS 中的堆内存的创建和释放
JS 引擎中的内存分类:
- 栈内存(作用域)供js代码执行的环境,保存基本数据类型
 - 堆内存:存储引用数据类型
 
堆内存的创建和销毁
创建:
创建一个对象、数组、函数、等引用数据类型,浏览器都会分配一块堆内存地址,存储引用数据类型的数据;
var obj = {name: '珠峰培训',age: 10,courses: ['高级前端课程', '前端架构师课程', 'UI设计师课程']}; // 每创建一个引用数据类型,浏览器都会开辟一个堆内存,并把这个堆内存地址给 obj 变量
// 此时 obj 相当于引用了这一块堆内存,var obj2 = obj; // 此时 obj2 也引用了这一块堆内存
堆内存释放
让所有引用堆内存空间地址的变量赋值为 null 即可(没有变量占用这个堆内存了,浏览器会在空闲的时候把它释放掉)
obj = null; // 将 obj 指向空对象指针 nullobj2 = null; // 将 obj2 指向空对象指针 null
js中的栈内存开辟和释放
- 栈内存(作用域):供 js 代码执行的环境,也是用来保存基本数据类型的
 
栈内存的创建:
- 当浏览器打开时,首先会开辟形成一个顶层的栈内存,就是全局作用域;
 - 函数执行时,也会开辟一个供函数代码执行的栈内存(私有作用域)
 
function add(a, b) {var total = 0;total = a + b;return total;}
函数每一次执行,都会形成一个全新的栈内存,即每次函数执行都是在一个全新的环境里面执行,所以函数每次执行都是互相独立的;
add(1, 2);add(1, 3);add(1, 2);function fe() {return {zf: 'zhufeng'}}var o1 = fe(); //var o2 = fe();console.log(o1 === o2); // false fe 执行几次,救护创建几个对象,所以 o1 和 o2 是两个不同的堆内存,没有任何关系。
栈内存销毁:
- 全局栈内存:当页面关闭时才会销毁
 - 函数的私有作用域:一般函数执行完成后,栈内存自动销毁。但是有一些特殊情况需要注意;
 
栈内存不销毁的情形
- 函数栈内存:正常情况下,函数执行会形成一个栈内存(作用域),当函数执行完成就会自动销毁。
 - 但是函数执行完成后,当前形成的栈内存中,某些内容被栈内存以外的变量占用了,此时栈内存不能释放(一旦释放外面找不到原有的内容了)。栈内存不销毁,保存在栈内存中的数据也不会被销毁
 
1 函数返回值被占用
function fe() {return {name: "珠峰培训"}}var obj = fe(); // 函数 fe 执行形成一个不销毁的栈内存,里面定义的对象被外部 obj 变量占用,因而作用域不销毁fe(); // 他们正常形成栈内存,但是执行后会被销毁
2.函数内部的引用数据类型被外部占用,函数执行的作用域不销毁。
var x = null;function fn() {x = {name: 'zf'}};fn(); // 此时 x 占用着 fn 的作用域中对象 {name: 'zf'}
累加计数
- 需求:实现一个累加计数的功能,点击一次,让按钮中的数字累加;
 
var btn = document.getElementById('btn');
- 思路:既然是累加,一定是有一个地方保存着它的上一个值。关键就在于保存在哪里。
 
1. 把值保存在全局作用域
var count = 0; // count 保存在全局作用域btn.onclick = function () {count++; // 操作全局变量btn.innerHTML = count;};
2. 自定义不销毁的私有作用域
btn.onclick = (function () {var count = 0; // count 的值保存在return function () {btn.innerHTML = count;}})();
3. 自定义不销毁的函数作用域2
(function () {var count = 0; // count 保存在自执行函数形成的私有作用域中btn.onclick = function () {count++;this.innerHTML = count;}})();
4. 块级作用域
{let count = 0; // let 会把花括号变成块级作用域,等效于私有作用域,count 的值保存在块级作用域中btn.onclick = function () {count++;this.innerHTML = count;}}
let & const
let const 是 ES6 新增关键字,用于声明变量
- let 用来声明普通变量
 - const 用于声明常量
 
let & const 和var的区别
- let const 不存在变量提升,对于使用 let const 声明的变量,只能在定义后使用
 
console.log(a); // 报错:a is not definedlet a = 0;console.log(b); // 报错:b is not definedconst b = 12;
2. let const 不能重复声明变量(重复声明 var 或者 function 声明的也不行)
var n = 1;var n;var n = 2;let n = 1; // 报错:n has already been declared
3. let const 在全局声明的变量或者常量不会像window上增加属性
var num = 100;console.log('num' in window); // true in 运算符,检测对象是否有某个属性let css = 'css3';console.log('css' in window); // false
4. let const 在代码块中会出现,会形成块级作用域,并且出现 暂时性死区;
- if (condition) { 条件的花括号中使用 let const 就会形成块级作用域 }
 
let num1 = 2;if (true) {let num1 = 4;console.log(num1); // 4}
- for(…) { for 循环中的代码块 }
 
for (var i = 0; i < 12; i++) {console.log('var 没有块级作用域')}console.log(i); // var 会把 i 泄露成全局变量for (let j = 0; j < 3; j++) {console.log('let j j不会泄露出去')}console.log(j); // Uncaught ReferenceError: j is not defined
- 利用 {} 形成块级作用域
 
{let a = 1;console.log(a); // a 是块级作用域中}{let m = 12; // m 所处的块级作用域是下面的块级作用域的上级作用域,同样遵从作用域链的查找规则{console.log(m);}}
暂时性死区(TDZ: temporary dead zone)
在代码块中,用 let const 声明的变量,不能再声明之前使用。
let q = 12;if (true) {console.log(q); // 这个变量 q 已经被 let 锁定在块级作用域中,let q = 14;}
const 声明变量的细节问题
1. const 声明时必须赋值
let m;var m;const m; // 报错:Missing initializer in const declaration
2. 值一旦被定义,不能被修改
const num = 12;num = 15; // 报错:Assignment to constant variable.
3. 如果 const 声明的常量是一个引用数据类型,那么常量带表的引用地址不能改变,但是堆内存中的内容是可以修改的。
const ary = [1, 2, 3];ary.push('33');console.log(ary);// ary = [1, 3, 5]; // 报错:
【发上等愿,结中等缘,享下等福,择高处立,寻平处住,向宽处行】
