变量声明,作用域 和 this 的碎碎念
变量声明
- ES5 中使用 var 或者 function 声明一个变量
- ES6 中声明变量的方式比较多:let, const,import,class 。
var 和 变量提升
使用 var 声明变量会存在变量提升的现象
console.log(a) // undefined
var a = 1
实际上JS的编译器在遇到代码 var a = 1 时会先进行 LHS ,向当前作用域查找是否已经存在一个名称为 a 的变量
如果有则忽略该声明,如果没有则会在当前作用域集合中创建一个新的变量,名为 a 。
编译器接下来还会生成引擎运行时需要的代码,用来处理赋值操作 a = 1。
以上是JS引擎运行前,编译器在编译过程中的处理。
在引擎运行时,处理 a = 1 ,先进行 RHS,查找当前作用域是否有名称为 a 的变量。如果有则赋值。如果没有则向上级作用域查找。如果最终也没有找到变量则报错 Uncaught ReferenceError: a is not defined
ps:RHS 查询是查找并取得某个变量的值。LHS 查询是找到要赋值的位置。
let / const 和 块级作用域
使用 let 和 const 声明的变量只在当前的代码块中有效,绑定在当前的块级作用域中。在此之前的ES5中只有全局作用域和函数作用域。
{
var a = 1;
let b = 2;
}
console.log(a) // 1
console.log(b) // Uncaught ReferenceError: b is not defined
值得注意的是在 循环中的应用
let arr = []
for(let i=0; i<10; i++){
console.log(i) //0, //1, //2, ... //9
arr[i] = function () {
console.log(i)
}
}
arr[0]() //0
arr[1]() //1
.
.
.
console.log(i) // Uncaught ReferenceError: i is not defined
由此可见,i 只存在于循环内部。这解决了以前 var 循环的问题
var arr = []
for(var i=0; i<10; i++){
console.log(i) //0, //1, //2, ... //9
arr[i] = function () {
console.log(i)
}
}
arr[0]() //10
arr[1]() //10
.
.
.
console.log(i) //10
由于 i 是由 var 声明的会提升到全局作用域,全局作用域下只有一个变量 i ,每一次循环都是同一个变量 i ,函数中console.log(i) 中的 i 都是全局作用域下的那个 i。所以,最后的 i 累加到了 10。
然而由 let 声明的 i 只在当前的代码块中有效,只在当前的循环中有效,每次循环都是一个新声明的变量 i 。之前的值由JS引擎内部记住用来初始化本次循环的 i 。
ps: for 循环的作用域中,循环变量的定义作为父级作用域,循环内部作为单独的子作用域使用。
暂时性死区(TDZ)
在块级作用域中使用 let / const 声明一个变量之后,该变量就绑定了这个块级作用域。凡是在 let / const 声明之前使用该变量的,都会报错
{
console.log(a) // Uncaught ReferenceError: a is not defined
let a = 1
}
在 let 之前的这个区域就成为 暂时性死区 ( temporal dead zone / TDZ )