1. Scoping rules / 块级作用域
var
在全局中生效,而let
和const
只在{}
内生效
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar); // Foo Bar
{
var moo = "Mooo"
let baz = "Bazz";
console.log(moo, baz); // Mooo Bazz
}
console.log(moo); // Mooo
console.log(baz); // ReferenceError
JS中的for
循环体比较特殊,每次执行都是一个全新的独立的块作用域
for (var i = 0; i <10; i++) {
setTimeout(function() { // 同步注册回调函数到 异步的 宏任务队列。
console.log(i); // 执行此代码时,同步代码for循环已经执行完成
}, 0);
}
// 输出结果
10 共10个
i
虽然在全局作用域声明,但是在for
循环体局部作用域
中使用的时候,变量会被固定
,不受外界干扰。
for (let i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i); // i 是循环体内局部作用域,不受外界影响。
}, 0);
}
// 输出结果:
0 1 2 3 4 5 6 7 8 9
ES6
明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域
。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”temporal dead zone
,简称 TDZ
2. Hoisting / 变量提升
使用var
时, 变量会在声明前初始化为undefined
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
而let
不会初始化,会报错ReferenceError
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
因为变量提升
,
当在最外层函数的外部声明var
变量时,作用域是全局的。 这意味着在最外层函数的外部用var
声明的任何变量都可以在windows
中使用。
At the top level, let
, unlike var
, does not create a property on the global object:
var foo = "Foo"; // globally scoped
let bar = "Bar"; // not allowed to be globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
3. Redeclaration
In strict mode, var
will let you re-declare the same variable in the same scope while let
raises a SyntaxError.
let
在同一作用域里只能声明一次。
'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
4.const 不能被修改并且不能被重新声明
const greeting = 'say Hi';
greeting = 'say Hello instead'; // error: Assignment to constant variable.
const greeting = 'say Hi';
const greeting = 'say Hello instead'; // error: Identifier 'greeting' has already been declared
当用const
声明对象时,这种行为却有所不同。 虽然不能更新const
对象,但是可以更新该对象的属性。 因此,如果我们声明一个const
对象为
const greeting = {
message: 'say Hi',
times: 4,
};
同样不能像下面这样做:
const greeting = {
words: 'Hello',
number: 'five',
}; // error: Assignment to constant variable.
但我们可以这样做:
greeting.message = 'say Hello instead';
最后,我们总结一下它们的异同:
var
声明是全局作用域或函数作用域,而let
和const
是块作用域。var
变量可以在其范围内更新和重新声明;let
变量可以被更新但不能重新声明;const
变量既不能更新也不能重新声明。- 它们都被提升到其作用域的顶端。 但是,虽然使用变量
undefined
初始化了var
变量,但未初始化let
和const
变量。 - 尽管可以在不初始化的情况下声明
var
和let
,但是在声明期间必须初始化const
。 source: