ES 6 中新增加了块级作用域,解决了ES 5中由于作用域不规范引发的各类问题。
一. 概述
- es5作用域和es6作用域比较? 答:① ES 5中只有全局作用域和函数作用域;② ES 6 中使用let命令实际为新增了块级作用域。在不同块中使用let命令定义变量互不影响。外层不能引用let内层定义变量,内层可以引用外层变量。
- 函数可以在块级作用域中声明,ES5中会变量提升至函数首部,ES6中类let方式,不同域中互不影响。
二. ES 5 中作用域以及缺点
1.ES 5作用域定义
-
ES5只有全局作用域和函数作用域。
-
使用var声明的变量会被自动添加到最接近的环境中。
-
如果初始化时没有使用var声明,该变量会自动被添加到全局环境中。
//全局变量 var scope=1 //函数中声明变量 function scopefun() { var scope=2 console.log(scope)//2 } //函数中变量不会影响到全局变量 console.log(scope)//1 //对象本质也是函数 var Vehicle = function () { var scope=4 //4 }; console.log(scope)//1
2.ES 5作用域缺点
-
第一种场景,内层变量可能会覆盖外层变量
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } //在f函数中出现了变量提升,所以不会打印外部的tmp变量 f(); // undefined
-
第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } //变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。 console.log(i); // 5
三. ES 6 中块级作用域
1.ES 6块级作用域
-
使用
let
命令实际为 JavaScript 新增了块级作用域。 -
在不同块中使用let命令定义变量互不影响。
-
外层不能引用let内层定义变量,内层可以引用外层变量。
function f1() { let n = 5; let f = 6; if (true) { let n = 10; let c = 10; //内部引用外部变量 console.log(f); // 6 } // console.log(c); // error 内部定义外部不能使用,变为未定义变量 console.log(n); // 5 }
-
块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不再必要了。
// IIFE 写法 (function () { var tmp = ...; ... }()); // 块级作用域写法 { let tmp = ...; ... }
四.块级作用域与函数声明
1.ES 5函数声明
- ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。
- 下面2种声明方式在ES5中都是非法的,但是浏览器在实现规范的时候为了兼容以前代码,上述声明时生效的。
// 情况一 if (true) { function f() {} } // 情况二 try { function f() {} } catch(e) { // ... }
2.ES 6函数声明
-
ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
-
为了兼容旧代理,ES6 的浏览器实现可不必严格遵守以上原则。有的浏览器遵守上述规;有的浏览器,会出现函数的变量提升,并不统一。
-
范例:
function f() { console.log('I am outside!'); } (function () { if (false) { // 重复声明一次函数f function f() { console.log('I am inside!'); } } f(); }());
-
如上范例在es5中:
// ES5 环境 function f() { console.log('I am outside!'); } (function () { //变量提升 function f() { console.log('I am inside!'); } if (false) { } f(); }());
-
如上范例在es6中:
// 浏览器的 ES6 环境 function f() { console.log('I am outside!'); } (function () { if (false) { // 重复声明一次函数f function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function //等同于如下 - > // 浏览器的 ES6 环境 function f() { console.log('I am outside!'); } (function () { var f = undefined; if (false) { function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function
3.函数在块级作用域声明总结
-
允许在块级作用域内声明函数。
-
函数声明类似于
var
,即会提升到全局作用域或函数作用域的头部。 -
同时,函数声明还会提升到所在的块级作用域的头部。
-
上面三条规则只对
ES6
的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let
处理。 -
应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
// 块级作用域内部的函数声明语句,建议不要使用 { let a = 'secret'; function f() { return a; } } // 块级作用域内部,优先使用函数表达式 { let a = 'secret'; let f = function () { return a; }; }