前言
- let、const、var的区别,什么是块级作用域,如何用?
在JavaScript中,一共存在3种声明变量的方式:let、const、var。
之所以有3种方式,这是由于历史原因造成的。最初声明变量的关键字就是var,但是为了解决作用域的问题,所以后面新增了let和const的方式。
作用域
ES5中的作用域有:全局作用域、函数作用域,ES6新增了块级作用域。
块级作用域是由{}包裹,if语句和for语句里面的{}也属于块级作用域。
var
- 没有块级作用域的概念
// Global Scope
{
// Block Scope
var a = 1
}
console.log(a) // 1
上面代码中,在Global Scope(全局作用域)中,且在Block Scope(块级作用域){}中,a输出结果为1,由此可以看出var声明的变量不存在Block Scope的概念。
- 有全局作用域和函数作用域
var a = 1
function fn() {
var a = 2
console.log(a) // 2
}
fn()
console.log(a) // 1
上面代码中,在Global Scope(全局作用域)中,声明了一个变量a,值为1,在函数fn中,又声明了一个变量a,值为2,所以输出结果为2,在函数fn执行完毕后,输出Global Scope(全局作用域)中的a,输出结果为1。
- 不初始化值默认为undefined
var a
console.log(a) // undefined
- 存在变量提升
console.log(a) // undefined
var a = 1
上面代码中,在声明变量a之前,就输出a的值,结果为undefined,这说明变量a已经声明了,但是没有赋值,默认为undefined,这就是变量提升。
所谓变量提升,就是将变量的声明部分提升到当前作用域的最顶端
所以上面的代码等价于
var a
console.log(a) // undefined
a = 1
- 全局作用域用var声明的变量会挂载到window上
var a = 1
console.log(window.a) // 1
console.log(a) // 1
console.log(this.a) // 1
上面代码中,在Global Scope(全局作用域)中,声明了一个变量a,值为1,然后输出window.a,结果为1,这说明在Global Scope(全局作用域)中,用var声明的变量会挂载到window上。
- 在同一个作用域中可以重复声明
var a = 1
var a = 2
console.log(a) // 2
上面代码中,变量a被声明了两次,第二次声明时,变量a的值被覆盖了,所以输出结果为2。
let
- 有块级作用域的概念
// Global Scope
{
// Block Scope
let a = 1
}
console.log(a) // ReferenceError: a is not defined
上面代码中,在Global Scope(全局作用域)中,且在Block Scope(块级作用域){}中,a输出结果为1,但是当在Global Scope(全局作用域)中输出a时,会报错,说明let声明的变量不存在Global Scope(全局作用域)中。
- 没有变量(声明)提升
console.log(a) // ReferenceError: Cannot access 'a' before initialization
let a = 1
上面代码中,在声明变量a之前,就输出a的值,结果为ReferenceError: Cannot access 'a' before initialization,这说明let声明的变量不存在变量提升。
- 暂时行死区
当程序的控制流程在新的作用域(module、function或block作用域)进行实例化时,在此作用域中用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。
简单理解就是ES6规定,let/const命令会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。
总之,在代码块内,使用let/const命令声明变量之前,该变量都是不可用的。
这在语法上,称为"暂时性死区"(temporal deadzone,简称TDZ)
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。
- 在同一个作用域中不允许重复声明
{
let a = 1
let a = 2 // Identifier 'a' has already been declared
}
{
let a = 1
var a = 2 // Identifier 'a' has already been declared
}
{
var a = 1
let a = 2 // Identifier 'a' has already been declared
}
const
const 跟 let 基本一致,但也有一些区别
- 声明时必须初始化
const a // SyntaxError: Missing initializer in const declaration
上面代码中,const命令声明变量a时,没有初始化,就会报错。
- 声明后不允许修改
const a = 1
a = 2 // TypeError: Assignment to constant variable.
上面代码中,const命令声明变量a时,初始化为1,然后对a赋值2,就会报错。
总结
- let、const、var的区别
var
没有块级作用域的概念
有全局作用域和函数作用域的概念
不初始化值默认为undefined
存在变量提升
全局作用域用var声明的变量会挂载到window上
在同一个作用域中可以重复声明
let
有块级作用域的概念
没有变量(声明)提升
暂时行死区
不存在全局作用域的概念
在同一个作用域中不允许重复声明
const
const 跟 let 基本一致,有两点区别
声明时必须初始化
声明后不允许修改
- 什么是块级作用域,如何用
ES5中的作用域有:全局作用域、函数作用域,ES6新增了块级作用域。
块级作用域是由
{}包裹,if语句和for语句里面的{}也属于块级作用域在以前没有块作用域的时候,在if或者for循环中声明的变量会泄露成全局变量,其次就是{}中的内层变量可能会覆盖外层变量。块级作用域的出现解决了这些问题。