概览
ES6 新增了两个定义变量的关键字:let
与 const
,它们几乎取代了 ES5 定义变量的方式:var
。let
是新的var
,const
简单的常量声明。
function f() {
{
let x;
{
// okay, block scoped name
const x = "sneaky";
// error, const
x = "foo";
}
// error, already declared in block
let x = "inner";
}
}
let
、const
实现块级作用域
let
,const
创建的变量都是块级作用域:它们只存在包围它们的最深代码块中。
function func() {
if (true) {
let tmp = 123;
// const tmp = 123;
}
console.log(tmp); // ReferenceError: tmp is not defined
}
console.log(tmp);// ReferenceError: tmp is not defined
相比之下,var
声明的是函数域。
function func() {
if (true) {
var tmp = 123;
}
console.log(tmp); // 123
}
func()
console.log(tmp); // tmp is not defined
面试题:循环中定时器闭包
for(var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i) //5, 5, 5, 5, 5
}, 0)
}
console.log(i) //5 i跳出循环体污染外部函数
//将var改成let之后
for(let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i) // 0,1,2,3,4
}, 0)
}
console.log(i)//i is not defined i无法污染外部函数
在for循环中使用var声明的循环变量,会跳出循环体污染当前的函数。
let
、const
暂时性死区(temporal dead zone)
let
,const
声明的变量拥有暂时性死区:当进入它的作用域,它不能被访问(获取或设置)直到执行到达声明。
简单描述:
if (true) {
//这块区域是TDZ
console.log(a) // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 1
// const a = 1
}
if (true) { // enter new scope, TDZ starts
// Uninitialized binding for `tmp` is created
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ ends, `tmp` is initialized with `undefined`
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
下面示例将演示死区(dead zone)是真正短暂时间的(基于时间)和不受空间条件限制(基于位置)
if (true) { // enter new scope, TDZ starts
const func = function () {
console.log(myVar); // OK!
};
// Here we are within the TDZ and
// accessing `myVar` would cause a `ReferenceError`
let myVar = 3; // TDZ ends
func(); // called outside TDZ
}
var
变量提升
JavaScript中,我们通常说的作用域是函数作用域,使用var声明的变量,无论是在代码的哪个地方声明的,都会提升到当前作用域的最顶部,这种行为叫做变量提升(Hoisting)
下面代码,演示了函数的变量提升:
{ // Enter a new scope
console.log(foo()); // hello, due to hoisting
function foo() {
return 'hello';
}
}
也就是说,如果在函数内部声明的变量,都会被提升到函数开头,而在全局的声明,就会提升到全局作用域的顶部。
function test() {
console.log('1: ', a) //undefined
if (false) {
var a = 1
}
console.log('3: ', a) //undefined
}
test()
实际执行时,上面的代码中的变量a会提升到函数顶部声明,即使if语句的条件是false,也一样不影响a的提升。
function test() {
var a
//a声明没有赋值
console.log('1: ', a) //undefined
if (false) {
a = 1
}
//a声明没有赋值
console.log('3: ', a) //undefined
}
在嵌套函数的情况,变量只会提升到最近一个函数的顶部,而不会到外部函数。
//b提升到函数a顶部,但不会提升到函数test。
function test() {
function a() {
if (false) {
var b = 2
}
}
console.log('b: ', b)
}
test() //b is not defined
let
不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
因此在函数内部不能重新声明函数
function func(arg) {
let arg;
}
func() // 报错 Identifier 'arg' has already been declared
function func(arg) {
{
let arg;
}
}
func() // 不报错
const
命令
一般使用场景:
const start = 'hi all';
const getName = () => {
return 'jelly';
};
const conf = {
fav: 'Coding'
};
// 模板
const msg = `${start}, my name is ${getName()}, ${conf.fav} is my favourite`;
你可能不知道的事:
// 1. 与引号混用
const wantToSay = `I'm a "tbfed"`;
// 2. 支持多行文本
const slogan =
`
I have a dream today!
`;
// 比较适合写HTML
const resultTpl =
`
...
`;
var
、let
、const
有什么区别
- 相同点:
var
,let
,const
声明的变量,是不能被delete
的; - 区别:
变量提升:var
声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined; let
,const
不存在变量提升,即它们声明的变量一定要在声明后使用,否则会报错。
暂时性死区:var
不存在暂时性死区;let
、const
存在暂时性死区,只有等声明变量后,才可以获取和使用该变量。
重复声明:var
允许重复声明;lat
、const
在同一作用域不允许重复声明。
修改声明的变量:var
和let
可以修改声明的变量;const
声明一个只读常量,一旦声明,常量的值就不能改变。