块级作用域
1. let 关键字与 var 关键字的区别
ECMAScript 6 新增了 let 关键字用于声明变量,该变量只能在指定的代码块内有效。
(1)块级作用域与函数作用域
如下代码所示:
{ let a = 100;//块级作用域 var b = 1 00;//函数作用域 } console.log(b)//100 console.log(a)//a is not defined
显而易见,结果是使用 let 声明的变量报错,var 声明的变量输出正确的值。
(2)暂时性死区(不存在声明提前)
ECMAScript 6标准明确规定,如果在一个代码块中使用 let 或 const 声明变量或常量时,当前代码块中对这些声明的变量或常量形成了一个封闭的作用域。
在这个代码块中,在使用 let 或 const 声明之前,该变量或常量都是不可用的。这种情况,在语法上被称为“暂时性死区”( Ternporal Dead Zone,简称为TDZ)。
使用 var 关键字声明变量时会出现声明提前的情况,就是变量在声明之前被访问,其值为undefined。
但 let 关键字不允许变量声明提前。
如下代码所示:
console.log(m);//undefined var m = 100; // var 关键字声明允许声明提前 console.log(v); let v = 100;//ReferenceError: Cannot access 'v' before initialization //let 关键字声明不允许声明提前
(3)let 不允许重复声明
let 关键字与 const 关键字一样,定义的变量不允许重复声明。
示例代码如下:
// 使用关键字var允许重复声明 var v = 100; console.log(v); var v = 1000;//重复声明 - 不报错 console.log(v); /* // 使用关键字 let 不允许重复声明 let m = 100; console.log(m); let m = 1000;//重复声明 - 报错 console.log(m);//SyntaxError: Identifier 'm' has already been declared */ // 使用关键字 let定义变量允许重新赋值 let m = 100; console.log(m); m = 1000;//重复赋值 console.log(m);//1000
值的注意的是:使用关键字 let 虽然不允许重复声明,但可以重新赋值。
(4)与函数的区别
// 1.使用var允许声明提前 /* var v = 100; function fn(){ console.log(v);//undefined var v = 1000; console.log(v);//1000 } fn(); */ // 2.使用let不允许声明提前 /* let v = 100; function fn(){ // 函数作用域封闭 - 全局作用域中的变量与当前函数作用域无关 console.log(v);//ReferenceError: Cannot access 'v' before initialization let v = 1000; console.log(v);//1000 } fn(); */ //3.函数作用域内 let v = 100;//全局变量 console.log(v);//100 // 自调函数 (function(){ let v ; // 函数作用域 console.log(v);//undefined })(); if (true){ console.log(v);//100 }
(5)与函数参数的区别
/*// 在ES6中函数的参数相当于使用let关键字定义的局部变量 function fn(a) { let a = 1000; console.log(a);//SyntaxError: Identifier 'a' has already been declared } fn(100);*/ /*function fn(a) { let a = 1000; console.log(a);//SyntaxError: Identifier 'a' has already been declared } fn(100);*/ function fn(a) { a = 1000;//允许重新赋值 console.log(a);//100 } fn(100);
在 ES 6 中函数的参数相当于使用 let 关键字定义的局部变量
2.为什么需要块级作用域?
在循环体中用于技术的变量泄露为全局变量
如下示例代码所示:
/* // 全局作用域 for (var i=0;i<10;i++){ console.log(i); } console.log(i);//10 用于计数的变量泄露为全局变量 */ // 块级作用域 for (let i=0;i<10;i++){ console.log(i); } console.log(i);//ReferenceError: i is not defined
上述示例代码中,关键字 var 定义的变量 i 只用于控制循环体的执行。当循环体执行结束后,变量 i 并没有释放,而是作为全局变量存在。而关键字 let 定义的变量 i 只用于控制循环体的执行。当循环体执行结束后,变量 i 释放。
经典面试题(循环体与数组的关系 )
/* var arr = []; for (var i=0;i<10;i++){ arr[i]=function () { return i; }; } console.log(arr[9]());//10 因为var关键字定义的变量i为全局变量 */ var arr = []; for (let i=0;i<10;i++){ arr[i]=function () { return i; }; } console.log(arr[6]());//6 因为let关键字定义的变量i为块级作用域
上述示例代码 var 关键字定义的i变量为全局变量,所有最后return的i的值都为10。
解析图如下:
3.块级作用域的函数声明
ECMAScript 5标准规定函数的声明只能在全局作用域和函数作用域中,不能在块级作用域中声明。
/* if (true){ // 块级作用域 var fun = function(){ console.log('this is function') } } // 全局作用域调用 fun(); */ // let 关键字声明函数 if (true){ // 块级作用域 let fun = function(){ console.log('this is function'); } } // 全局作用域调用 fun();//ReferenceError: fun is not defined
使用var关键字声明的函数可以在全局调用,但是使用let 关键字声明的函数不能再全局调用。