要想学习let和const命令,首先需要了解什么是块级作用域
ES5只有全局作用域和函数作用域,没有块级作用域,这就导致了很多不合理的场景
eg1、内层变量覆盖外层变量
var tmp = new Date();
function f() {
console.log(tmp);
if(false) {
var tmp = 'hello world';
}
}
f() // undefined
虽然函数f内的if代码块未执行,但是if代码块里重新声明了变量tmp,存在变量提升现象,导致内层的tmp变量覆盖了外层的tnp变量
eg2、用来计数的循环变量泄露为全局变量
var s = 'hello';
for (var i = 0; i< s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
上面代码中,变量i只用来控制循环。但是循环结束后,它并没有消失,而是泄露成了全局变量
相较于ES5,ES6新增了块级作用域。
let用于声明变量,const用于声明常量。它们的用法类似于var,但是所声明的变量只在这些命令所在的代码块内有效。
1、不存在变量提升
let和const不会像var那样会发生‘变量提升现象’。所以,变量和常量一定要在声明后使用,否则会报错。
这就意味着typeof不再是一个百分百安全的操作
console.log(typeof x);
let x;
在上面的代码中,由于typeof在运行时,x尚未声明,所以会抛出一个错误
2、暂时性死区
ES6明确规定,如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。只要在声明之前使用这些变量,就会报错。总之,在代码块内,使用let和const声明变量和常量之前,该变量和常量都是不可用的。这在语法上称为‘暂时性死区’
function bar (x = y, y = 2) {
return [x,y];
}
bar();
上面的代码最终会报错,是因为参数x的默认值等于另一个参数y,而此时y还没有声明,属于‘死区’。如果y的默认值是x,就不会报错,因为此时x已声明。
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
3、不允许重复声明
let和const不允许在相同作用域内重复声明同一个变量
因此也不能在函数内部重新声明参数
function func (num) {
let num
}
func(5);
上面代码会报错,下面代码则不会报错
function func (num) {
{
let num
}
}
func(6);
注意:
1、我们知道,const命令是用来声明常量的,但是对于符合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变。
const foo = {};
foo.prop = 123;
console.log(foo.prop); // 123
foo = {}; // TypeError: "foo" is read-only 不起作用
上面的代码中,常量foo储存的是一个地址,指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
2、跨模块常量
const声明的常量只在当前代码块内有效。如果想设置跨模块的常量,可以采用下面的写法。
// constants.js 模块
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模块
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模块
import { A, B } from './constants';
console.log(A); // 1
console.log(B); // 3
解释:export是暴露接口给其它文件,其它文件通过import加载export暴露出来的接口。test1中的import是将constants中通过export暴露出来的常量全部引用,test2中的import是只引用A,B常量
3、全局对象的属性
全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js中指的是global对象。在ES5中,全局对象的属性与全局变量是等价的。这种规定被视为JavaScript语言的一大问题,因为很容易不知不觉就创建了全局变量。ES6为了改变这一点,一方面规定,var命令和function命令声明的全局变量依旧是全局对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量不属于全局对象的属性
var a = 1;
window.a // 1
let b = 1;
window.b // undefined