1.新增两个关键字
- let
- const
2.var的缺陷
- 在ES6没有出来之前,ES5只有全局作用域以及函数作用域(局部作用域),这就会导致一些误会
对于作用域的理解,以及作用域链的理解
1.作用域:当一个变量被定义之后就会有相应的作用地方,也就是有一席之地,而它也只能影响它的一席之
地,不能去影响别的地方,也就是别的作用域
2.作用域链:作用域嵌套时,会产生作用域链条,它可以让嵌在里面的作用域可以去观察外面的作用域,而外
面的作用域不能观察嵌在里面的作用域
3.作用域的发展:ES6还没出来之前,只有全局作用域、函数作用域,ES6之后声明了块级作用域,也就是被{}括起来的部分就是块级作用域
3.什么是变量提升?(暂存性死区)
- 因JS执行顺序是从上到下,若在变量还没有被定义之前就使用变量,会造成一定的误会
- var 针对以上误会会做出变量提升的操作,也就是认定可以在变量没有被定义前就可以使用变量,但是得到的结果是undefined
- let会造成变量提升,但是系统不会对let的变量进行初始化,也就是不仅得不到undefined,而是会提示 Cannot access '变量名' before initialization,也就是说变量没有被初始化
- const跟let是同样的情况
- 变量提升
- 变量和函数声明会移至其作用域的顶部(重点理解)
总结:
var、let、const都有变量提升,但是var会初始化变量,但是let、const就不会初始化变量(也就是暂存性死区)
4.三种关键字的作用域?
- var
- 当var在最外层函数被定义时,表示全局变量,也就是可以被指定在任意地方使用
- 当var在函数内部被定义,表示局部变量,也就是说只能在函数内部被使用(除了闭包特殊情况)
- let
- 当let在最外层{}被定义时,表示全局变量,也就是可以被指定在任意地方使用
- 当let在{}被定义,表示块级作用域,也就是只能在{}内部被使用(除了闭包之外)
- const
- 当const在最外层{}被定义时,表示全局变量,也就是可以被指定在任意地方使用
- 当const在{}被定义,表示块级作用域,也就是只能在{}内部被使用(除了闭包之外)
总结:
1.新增加了 块级作用域 也就是 {} 大括号括起来的部分被称为 块级作用域
2.var 只有全局变量和局部变量之说,let、const则有全局变量、块级作用域之说
5.变量定义后是否能修改或者重复定义?
- var
- 可以被重复定义,不管是不是在相同作用域下
- 可以在相同作用域或者不同作用域内被修改
- let
- 在不同作用域下可以被重复定义,但是在相同作用域下不能被重复定义(指的是书写代码)
- 在不同作用域下可以被修改,在相同作用域下不可以被修改(指的是书写代码)
// 报错
let a = 10;
let a = 20;
// 不报错
let a = 20;
function fn(){
let a = 30;
return a;
}
console.log(fn()); // 30
console.log(a); // 20
// 从代码上看 a 在不同作用域下被修改了,其实输出值是互不影响的,也就是 a 的值并未真正的被修改,只是代码给你的感觉它被修改了
- const
- 在不同作用域下可以被重复定义,但是在相同作用域下不能被重复定义(指的是书写代码)
- 在不同作用域下可以被修改,在相同作用域下不可以被修改(指的是书写代码)
// 报错 (相同作用域下)
const a = 10;
const a = 20;
// 不报错 (不同作用域下)
const a = 20;
function fn(){
const a = 30;
return a;
}
console.log(fn()); // 30
console.log(a); // 20
// 从代码上看 a 在不同作用域下被修改了,其实输出值是互不影响的,也就是 a 的值并未真正的被修改,只是代码给你的感觉它被修改了
重点理解
const声明的是一个只读对象,也就是说不能被修改(下面具体说说修改情况)
- 能被修改的情况(引用数据类型)
- 当const定义的是一个对象时,对象里面的属性是可以被改变的,也就是可以被修改的
- 不能被修改的情况
- 当const定义的是一个“基础数据类型”时,常量不能被修改
- 当const定义的是一个“引用数据类型”时,对象不能被修改
- 例如:const foo = { } // foo 是不能被修改的,而{ }里面的内容可以被修改
6.堆内存和栈内存的理解
- 栈内存(stack)
- 基本数据类型保存在栈内存中,基本数据类型占用空间小、大小固定,通过按值来访问,属于频繁使用的数据
- 注意:
- 需要注意的是闭包中的基本数据类型变量不保存在栈内存中,而是保存在堆内存中
1.明白堆内存和栈内存的垃圾回收机制
栈内存中变量一般在它的当前执行环境结束就会被销毁被垃圾回收制回收, 而堆内存中的变量则不会,因为不确定其他的地方是不是还有一些对它的引用。 堆内存中的变量只有在所有对它的引用都结束的时候才会被回收。
2.闭包中的基本数据类型保存在堆内存中
因为闭包里面的变量使用之后不会被垃圾回收机制处理,所以存储在堆内存中,而不是栈内存中
// 对于闭包的理解
// 闭包的目的在于:让外面作用域能访问到里面的作用域
function A() {
let a = 1;
function B() { // 函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包
console.log(a);
}
return B;
}
let res = A(); // 这里的 res 就是闭包导致 a 变量的弹出,本来 a 是存在在块级作用域里的,因为 return 弹出函数 B
// 用全局变量 res 来接收,导致 a 的值被赋值在了 res上,我们访问在外面作用域就能访问到里面的块级
// 作用域里的 a 变量了,这样就导致了 a 其实已经变成了全局变量,永远不会被垃圾回收机制所清除了,也
// 就是被存储在了堆内存中,而不是栈内存中了
- 堆内存(heap)
- 引用数据类型存储在堆内存中,因为引用数据类型占据空间大、大小不固定
- 引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量中获取了该对象的地址指针, 然后再从堆内存中取得我们需要的数据
// 基本数据类型-栈内存
let a1 = 0;
// 基本数据类型-栈内存
let a2 = 'this is string';
// 基本数据类型-栈内存
let a3 = null;
// 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中
let b = { m: 20 };
// 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中
let c = [1, 2, 3];