ES6 新增了let
命令,所声明的变量,只在let
命令所在的代码块内有效。
关于let的要点
1.使用let
的for循环的特别之处:
设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。举例!
for (let i = 0; i < 3; i++) {
let i = "abc"
console.log(i);
}
//结果打印三次abc
函数内部的变量i
与循环变量i
不在同一个作用域,有各自单独的作用域(同一个作用域不可使用 let
重复声明同一个变量)。
2. 不存在变量提升
和var
不一样它所声明的变量一定要在声明后使用,否则报错。
3. 暂时性死区
如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。举例!
var tmp = 123;
if (true) {
tmp = "abc" //ReferenceError
let tmp
}
包括这种情况:
let x = x //ReferenceError
因为js是先取值再赋值,先RHS查询取得x的值然后LHS查询赋值给变量x,然而x的值并没有被声明,所以报错+
4.不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。举例!
function fn(){
let a = 10
var a = 20 //Uncaught SyntaxError: Identifier 'a' has already been declared
}
因此,不能在函数内部重新声明参数。
关于块作用域
es5只有全局作用域和函数作用域,没有块级作用域,就会出现这样的情况:
var tmp = new Date()
function fn(){
console.log(tmp);
if(false){
var tmp = "hello world"
}
}
fn() // undefined
这是由于变量提升导致的,if语句内的var tmp被提升到了函数fn中第一行
function fn() {
var tmp
console.log(tmp); // undefined
if (false) {
tmp = "hello world"
}
}
let
实际上为 JavaScript 新增了块级作用域。
function fn() {
let a = 5;
if (true) {
let a = 10
}
console.log(a);
}
fn() // 5
两个let创建的两个块作用域,两个a都是独立的互不干扰,原先匿名函数的的作用也被取代了
//匿名函数写法
(function () {
var a = 10
}())
//块级作用域写法
{
let a = 10
}
块级作用域与函数声明
ES5规定函数只能在全局作用域和函数作用域之中声明,不能在块级作用域声明。但ES6允许在块级作用域之中声明函数,函数声明语句的行为类似于let
,在块级作用域之外不可引用。关于这一点,不同的支持环境会导致不同的结果,应该避免在块级作用域内声明函数。
顺便一提,ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
关于const命令
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI=3.1415
PI=3 // Uncaught TypeError: Assignment to constant variable
这也意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。举例!
const PI; // Uncaught SyntaxError: Missing initializer in const declaration
const
的作用域与let
命令相同:只在声明所在的块级作用域内有效。
{const PI=3.1415}
console.log(PI); //Uncaught ReferenceError: PI is not defined
同样的,const
命令声明的常量也是不进行提升,同样存在暂时性死区,只能在声明的位置后面使用,也与let
一样不可重复声明。
const本质
const
实际上保证的,并不是变量的值不得改动,而是变量指向的内存地址不得改动。因此对于复杂类型的数据,const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。举例!
const foo = {}
foo.name = "哈哈" // 哈哈
console.log(foo.name);
foo = {} // Uncaught TypeError: Assignment to constant variable.
foo
储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,但对象本身是可变的,所以依然可以为其添加新属性。
顺便一提:let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。(在浏览器中顶层对象就是window)
本文参考自《ES6入门标准》一书