声明方式 | 作用域 | 变量提升 | 初始赋值 | 重复声明 | 改变值 |
---|---|---|---|---|---|
var | 函数作用域 | 有 | 不需要 | 允许 | 可以 |
let | 块作用域 | 有 | 不需要 | 不允许 | 可以 |
const | 块作用域 | 有 | 需要 | 不允许 | 不可以 |
改变值
用 var
/let
声明的是变量,可改变值
用 const
声明的是常量,不可改变值
作用域
用 var
声明的变量不允许在其所在函数作用域之外使用
用 let
/const
声明的变量/常量不允许在其所在块级作用域之外使用
变量提升
用 var 声明的变量有变量提升,并且默认赋值为 undefined
,所以在变量声明之前获取变量的值为 undefined
用 let
/const
声明的变量/常量也有变量提升,但是不会被初始化赋值,不能被引用。因为 ES6 规定,如果区块中存在 let
和 const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域(暂时性死区)。凡是在声明之前就使用这些变量,就会报错
下面用一个例子来证明:
let a = 1;
(function () {
console.log(a); // 1
// let a = 3;
})()
可以看到,注释之后,由于函数的作用域链,在闭包内没有找到 a
的定义,沿着作用域链找到外层了的 a
,接下来,去掉注释:
let a = 1;
(function () {
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 3;
})()
注意:这里报错提示是在初始化前不允许读取 a
,而不是 a is not defined
可见,由于 let a = 3
在闭包作用域内的变量提升,阻断了函数作用域链的向上延伸。但在闭包内部,尽管 a
发生了变量提升,但是在初始化赋值之前(before initialization)不允许读取
初始赋值
用 var/let 声明的变量在声明时不赋值,值为 undefined
用 const 声明的常量在声明时必须赋值,否则报错
全局作用域
在全局作用域下用 var 声明的变量会被挂载到 window 上,成为 window 对象的属性
在全局作用域下用 let/const 声明的变量不会会被挂载到 window 上,不会成为 window 对象的属性