let和const
基本知识
🥦仅在声明的代码块内有效,包括嵌套的代码块
🍧不存在变量提升:使用未声明的let/const变量会报错ReferenceError;而使用未声明的var变量值为undefined
🧃会发生暂时性死区:一进入当前作用域,所要使用的变量就已存在,但在声明变量之前不可获取;而在父级已声明该变量时,子块内也重新声明该变量时,也会发生暂时性死区(报错ReferenceError)
- 此时需要保证在声明之后再使用该变量
🥩同一代码块内不允许重复声明:
- 🥨函数参数与函数体属于同一个作用域,在函数体内重新对参数声明会报错
- 🥣for循环参数与循环体是两个不同的作用域,所以在循环参数内声明let,然后再次循环体声明let,不会报错
🍇const声明必须初始化,且声明后不能改变
- const保证的是变量指向的内存地址不变,而不能保证变量的数据结构不变(对象内部是可变的),若想使对象内部也不可变,可使用**冻结函数(freeze等)**进行深层次遍历
案例:
javascript
var a = []
// 整个代码中,变量i只有一个
// 🍋:在整个代码块中,变量i已泄露到整个作用域
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
// 这里调用,会按序输出
a[i]()
}
// 这里调用,由于整个代码中,仅有一个变量i,此时变量i的值已为10
a[8]()
// 🥦
const b = []
// 每次循环,都会声明一个新的变量i
for (let i = 0; i < 10; i++) {
b[i] = function () {
// 这里的i每次都会更新
console.log(i)
}
// 这里调用,会按序输出
b[i]()
}
// 这里调用,会按序输出
b[8]()
// 🥣
for (let i = 0; i < 10; i++) {
// 重新赋值,不会报这个错Uncaught SyntaxError: Identifier 'i' has already been declared
let i = Math.random()
console.log(i)
}
// 🧃
let c = 0
{
// ReferenceError: Cannot access 'c' before initialization
c = 9
let c
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
块级作用域
- 🍼使用块级作用域的原因:
- 内部变量不会覆盖外部变量;而var声明的可能会覆盖外部变量
- 🍋用于计数的循环变量不会泄露到全局
- 🍚可以替代匿名IIFE(立即执行函数表达式)
- 🍎ES6允许在块级作用域中声明函数,在ES6中,函数声明类似var(仅支持支持es6的浏览器,为了避免环境差异,应避免这种方式,可用函数表达式替代),具体如下:
- 会提升到所在全局/函数作用域的头部
- 会提升到所在块作用域的头部
- 🍷若无括号(如if语句),则引擎会认为不存在块级作用域;严格模式下,函数只能声明在作用域顶层
案例:
javascript
// IIFE
(function () {
var tmp = {}
// ......
}())
// 🍋:块级作用域
{
let tmp = {}
// ......
}
// 🍎
function f() { console.log('I am outside!'); }
(function () {
// 块作用域的f会提升到这里(函数作用域的头部)
// var f = undefined
if (false) {
// 也会提升到这里(块级作用域的头部)
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
// 🍷:报错SyntaxError: Lexical declaration cannot appear in a single-statement context
if (true)
let d = 1
// 🍷:严格模式下,报错SyntaxError: In strict mode code, functions can only be declared at top level or inside a block.
// 由于函数必须定义在顶层,所以在if语句内会报错
'use strict';
if (true)
function f() {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
扩展知识
- 🍌ES6声明变量的方式:var、function、let、const、import、class
- 🍖顶层对象的属性变动:
- ES5中,顶层对象的属性window(browser)、global(node)与全局变量等价,会造成一些问题:
- 无法在编译时就报出变量未声明的错误异常,只在运行时才知道
- 会不知不觉创建全局变量
- 顶层对象的属性是到处可读写
- ES6中,进行了改进,如下:
- var、function声明的全局变量是顶层对象的属性
- let、const、class等声明的变量不属于顶层对象的属性
- ES5中,顶层对象的属性window(browser)、global(node)与全局变量等价,会造成一些问题:
- 🍡glocalThis对象:ES2020中,其(node、browser)作为顶层对象,指向全局的this
- 🍳顶层对象:
- 浏览器,顶层对象是window,self也指向顶层对象
- web worker:self指向顶层对象
- node:顶层对象是global
- nodejs模块this返回的是当前模块,es6模块返回的是undefined