let和const
1.let只在它的代码块中才有效果。
//错误的写法 { let num = 12; } console.log(num)//报错 //正确 { let num = 12; console.log(num)// 12 }
2.不存在变量提升
// var 的情况 console.log(foo); // 输出undefined var foo = 2; // let 的情况 console.log(bar); // 报错ReferenceError let bar = 2;
3.for循环单独输出,并且for循环设置变量是父作用域,代码块里面是子作用域,两个是不关联的。
for (let i = 0; i < 3; i++) { let i = ‘abc‘; console.log(i);//3次abc }
4.暂时性死区
简单来说就是只要在一个代码块内,在声明let变量前就使用这个变量,就会报错,
var 在前面 { var tmp = 12; //或者 全局变量 tmp = 12; let tmp = 12; console.log(tmp)//ReferenceError 都会报错 } //var 在外面 var tmp = 12; //或者 全局变量 tmp = 12; { let tmp = 12; console.log(tmp)//不会报错 } 局部var 在后面 { let tmp = 12; var tmp = 12; console.log(tmp)//Identifier ‘tmp‘ has already been declared } 全局var 在后面 { let tmp = 12; tmp = 12; console.log(tmp)//12 }
只要在let语句之前使用它声明的变量,都基本会报错。
typeof x //报错
let x;
比较隐蔽的暂时性死亡区域
function holle(x=y,y=2){
return [x,y]; //y还没有定义,所以会报错。
};
function holle(x=2,y=x){
return [x,y]; //[2,2]; y已定义。
};
var x = x; //不报错
let x = x; //报错,因为let语句是要在声明完成前才可以使用变量,这个还没有执行完成前就取x值了。
es6之所有这个规则是因为,在es5中很多都是在声明变量前就是用变量了,导致很多错误。
5.不允许重复声明
function ng(arg){ let arg = "arg"; console.log(arg);//报错 不允许重复声明 }; { let abc = "abc"; let abc = "123"; console.log(abc)//不允许重复声明。 } function ng(arg){ { let arg = "arg"; console.log(arg);//arg } };
6.块级作用域(es5只有函数作用域和全局作用域);
//块级作用域 function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 } f1() 这段代码表明了,es6的块级作用域,变量不会提升,如果换做var,打印出来的就是10了, 并且还有一个点就是,在块级作用域中,外部无法访问内部的变量,内部可以访问外部的变量。 所以我们在写es6时可以摒弃IIFE写法了。 //IIFE (function () { var tmp = ...; ... }()); // 块级作用域写法 { let tmp = ...; ... }
7.块级作用域与函数声明
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。
// 情况一 if (true) { function f() {} } // 情况二 try { function f() {} } catch(e) { // ... } 这两种情况都是非法的,但是浏览器没有遵循这个规则,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。 ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。 function f() { console.log(‘I am outside!‘); } (function () { if (false) { // 重复声明一次函数f function f() { console.log(‘I am inside!‘); } } f(); }()); 这段代码在es5中,不会报错,相当于var 在函数中声明。所以实际上应该是这样子的 (function () { //function f() { console.log(‘I am inside!‘); } if (false) { // 重复声明一次函数f } f(); }()); 如果在es6上,理论是得到 ‘I am outside!‘,但是在es6浏览器中会报错的,因为在es6浏览器中将遵循以下三条规则: 1.允许在块级作用域内声明函数。 2.函数声明类似于var,即会提升到全局作用域或函数作用域的头部。 3.同时,函数声明还会提升到所在的块级作用域的头部。 这规则只在es6浏览器有效。 如果确实需要可以使用函数表达式,而不是函数声明语句。 // 函数声明语句 { let a = ‘secret‘; function f() { return a; } } // 函数表达式 { let a = ‘secret‘; let f = function () { return a; }; } 另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。 // 不报错 ‘use strict‘; if (true) { function f() {} } // 报错 ‘use strict‘; if (true) function f() {}
8.do表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。
//封装没有返回值 { let t = f(); t = t * t + 1; } 使用do表达式,获得返回值 let x = do { let t = f(); t * t + 1; };