1. 为什么需要块级作用域?
-
第一种场景,内层变量可能会覆盖外层变量。
var tmp = new Date(); function f(){ console.log(tmp); if(false){ var tmp = 'hello world'; } } f();//undefined
-
第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello'; for(var i = 0;i<s.length;i++){ console.log(s[i]); } console.log(i);//5
2. ES6的块级作用域
-
let
实际上为JavaScript新增了块级作用域。 -
ES6允许块级作用域的任意嵌套。
-
内层作用域可以定义外层作用域的同名变量。
-
块级作用域的出现,实际上使得广泛应用的匿名立即执行函数表达式(IIFE)不再必要了。
3. 块级作用域与函数声明
-
ES5规定,函数只能在
顶层作用域
和函数作用域之中声明
,不能在块级作用域声明。//情况一 if(true){ function f(){} } //情况二 try{ function f(){} }catch(e){ //... } //上面两种函数声明,根据ES5的规定都是非法的。 //但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。
ES6引入块级作用域,明确允许在块级作用域之中声明函数。ES6规定,块级作用域之中,函数声明语句类似于
let
,在块级作用域之外不可引用。function f(){ console.log('I am outside!'); } (function (){ if(false){ function f(){ console.log('I am inside!'); } } f(); }) //ES5环境 //'I am inside!' //ES6环境 // Uncaught TypeError: f is not a function
ES6有块级作用域,为什么还会这样呢?
原来,如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6 在附录 B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。
但是三条规则只针对
ES6环境
。-
允许在块级作用域内声明函数。
-
函数声明类似于
var
,即会提升到全局作用域或函数作用域的头部。 -
同时,函数声明还会提升到所在的块级作用域的头部。
考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句
// 块级作用域内部的函数声明语句,建议不要使用 { let a = 'secret'; function f() { return a; } } // 块级作用域内部,优先使用函数表达式 { let a = 'secret'; let f = function () { return a; }; }
另外,还有一个需要
注意
的地方。ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。 -