1、新增let命令
与var不同的是:let声明的变量只在let命令所在的代码块内有效。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
有一种情况很适合用let命令:for循环的计数器。
for(let i =0; i<arr.length; i++){
}
console.log(i) //ReferenceError: i is not defined
下面就用一个例子来比较一下let和var在for循环计数器的不同,为什么let更适合:
var a = [];
for(var i = 0; i < 8; i++){
a[i] = function(){
console.log(i);
}
}
a[5](); // 8
var a = [];
for(let i = 0; i < 8; i++){
a[i] = function(){
console.log(i);
}
}
a[5](); // 5
2、只要块级作用域内存在let命令,那么它所声明的变量就不再受外部的影响,形成了封闭作用域
if (true){
//封闭作用域开始
tmp = 'abc'; //ReferenceError
console.log(tmp); //ReferenceError
let tmp;
//封闭作用域结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); //123
}
在let命令声明变量tmp之前,都属于变量tmp的"死区"。
此处需要注意一下:
函数的作用域是其声明时所在的作用域。
来个例子思考一下:
let foo = 'outer';
function bar(func = x => foo){
let foo = 'inner';
console.log(func()); // outer
}
bar();
func 默认是一个匿名函数,返回值为foo,作为bar函数的参数。func的作用域不是bar,func声明是在外层作用域,所以bar内部的foo指向函数体外的foo的值,这段代码等同于下面这段代码:
let foo = 'outer'
let f = x => foo;
function bar(func = f){
let foo = 'inner';
console.log(func()); //outer
}
bar();
总之,暂时性死去的本质就是:只要已进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
3、新增块级作用域
ES5只有全局作用域和函数作用域,这样会产生很多不合理的场景。
第一种场景:内层变量可能会覆盖外层变量
var tmp = "hello";
(function(){
console.log(tmp);
var tmp = "world";
})(); //undefined
思考一下,为什么结果会是undefined呢?
第一种情况可能就是函数内的tmp变量覆盖了全局tmp,如果去掉function我们看看变量的结果:
var tmp = "hello";
if(true){
console.log(tmp);
var tmp = "world";
} // hello
第二种情况就是存在变量提升,这又是怎么回事呢,也就是说最初的代码等同于下面这段代码:
var tmp = "hello";
(function(){
var tmp; //变量提升
console.log(tmp);
tmp = "world";
})(); //undefined
第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello';
for (var i = 0; i < s.length; i++){
console.log(s[i]);
}
console.log(i); // 5
变量i只是用来控制循环的,循环结束它就没有用了,但是却泄露成了全局变量,这个很不好。
可以说let为js增加了块级作用域,来看下面这段代码吧:
function a(){
let s = 1;
if(true){
let s = 3;
}
console.log(s); //1
}
function a(){
var s = 1;
if(true){
var s = 3;
}
console.log(s); //3
}
let 声明的变量,使外层的代码不受内层的代码影响,而var则没有这样的效果。
ES6允许块级作用域的任意嵌套,并且外层作用域无法读取内层作用域的变量
{{{{
{let s = 'Hello World'}
console.log(s); // 报错
}}}};
内层作用域可以定义外层作用域的同名变量
{{{{
let s = 'Hello World';
{let s = 'Hello World';}
}}}};
块级作用域可以代替立即执行函数
//立即执行函数
(function(){
var tmp = ...;
...
}());
//块级作用域
{
let tmp = ...;
...
}
ES6中函数本身的作用域在其所在的块级作用域之内
function f() { console.log('I am outside!'); }
(function () {
if(false) {
function f() { console.log('I am inside!'); }
}
f();
}());
ES5中的运行结果:I am inside!
ES6中的运行结果:I am outside!
造成这样结果的原因就是:
在ES5中存在变量提升,不论会不会进入if代码块,函数声明都会提升到当前作用域的顶部得到执行.
在ES6中支持块级作用域,不论会不会进入if代码块,其内部声明的函数皆不会影响到作用域的外部
{
let a = 'hello';
function b(){
return a;
}
}
f(); //报错
因为函数的调用在块级作用域的外部,所以无法调用块级作用域内部定义的函数。
let b;
{
let a = 'world';
b = function () {
return a;
}
}
b() // "world"
如果代码进行上面这样的处理,即把函数b的声明与函数调用放在同一块级作用域就可以了。
注:如果在严格模式下,函数只能在顶层作用域和函数内声明,其他情况(比如if代码块、循环代码块)的声明都会报错。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。