块级作用域
ES5中只有全局作用域和函数作用域,没有块级作用域,
1 //内层变量可能覆盖外层变量
2
3 var tmp = new Date();
4
5 function f() {
6 console.log(tmp);
7 if (false) {
8 var tmp = 'hello world';
9 }
10 }
11
12 f(); // undefined
13
14 // 用来计算的循环变量泄漏为全局变量
15 var s = 'hello';
16
17 for (var i = 0; i < s.length; i++) {
18 console.log(s[i]);
19 }
20
21 console.log(i); // 5
ES6中let const 实际上为JavaScript新增了的块级作用域,如果使用let,上述f()就会报错, console.log(i)则会报错,i is not defined
ES6 允许块级作用域的任意嵌套
1 {{{{
2 {let insane = 'Hello World'}
3 console.log(insane); // 报错
4 }}}};
5
6 {{{{
7 let insane = 'Hello World';
8 {let insane = 'Hello World'}
9 }}}};
块级作用域的出现,实际上使得广泛应用的匿名自执行函数表达式(匿名IIFE)不再必要
1 // IIFE 写法
2 (function () {
3 var tmp = ...;
4 ...
5 }());
6
7 // 块级作用域写法
8 {
9 let tmp = ...;
10 ...
11 }
块级作用域与函数声明
ES5 中规定, 函数只能在顶层作用域和函数作用域之中声明, 不能在块级作用域声明,
ES6引入块级作用域,明确运行在块级作用域中声明函数, ES6 规定块级作用域之中,函数声明语句的行为类似let,在块级作用域之外不可引用
1 function f() { console.log('I am outside!'); }
2
3 (function () {
4 if (false) {
5 // 重复声明一次函数f
6 function f() { console.log('I am inside!'); }
7 }
8
9 f();
10 }());
上面的代码在ES5中运行,会得到"I am inside!", 因为在if内声明的函数f会被提升到函数的头部
而在ES6中就完全不一样了,因为块级作用域内声明的函数类似于let, 对于作用域之外没有影响, 但是运行一下上面的代码就会报错
1 // 浏览器的 ES6 环境
2 function f() { console.log('I am outside!'); }
3
4 (function () {
5 if (false) {
6 // 重复声明一次函数f
7 function f() { console.log('I am inside!'); }
8 }
9
10 f();
11 }());
12 // Uncaught TypeError: f is not a function
报错原因:
因为如果改变了块级作用域内声明的函数的处理规则,那么对老代码会产生很大的影响,为了减轻因此产生的不兼容问题, ES6 中规定, 浏览器的实现可以不遵守上面的规定,而是有自己的行为方式,即:
1\ 允许在块级作用域内声明函数, 2\ 函数声明类似var, 即会提升到全局作用域或函数作用域的头部, 3\ 勇士函数声明还会提升到所在块级作用域的头部
注意 上面三条规则只对ES6的浏览器实现有线掐环境不遵守, 还是将块级作用域的函数声明当做let处理
由于环境导致的行为差异太大, 还是应该避免在作用域内声明函数, 如果确实需要的话, 也应该写成函数表达式
还需要注意的是, ES6的块级作用域必须有大括号, 如果没有JavaScript就认为不存在块级作用域
1 // 第一种写法,报错
2 if (true) let x = 1;
3
4 // 第二种写法,不报错
5 if (true) {
6 let x = 1;
7 }
顶层对象的属性
顶层对象在浏览器环境指的是window对象,在Node中值得是global对象,ES5中, 顶层对象的属性与全局变量是等价的, 这样会造成很多问题, 首先没法在编译时就报出变量未声明的错误, 只有运行时才能知道,其次程序员也可能不知不觉创建了全局变量,最后顶层对象的属性到处可以读写,这非常不利于模块化编程
ES6 为了改变这一点,一方面规定为了兼容性, var命令和function命令声明的全局变量,依旧是顶层对象的属性,另一方面规定, let命令, const命令, class命令声明的全局变量, 不属于顶层对象的属性, 也就是说ES6开始全局变量将逐步与顶层对象的属性脱钩
1 var a = 1;
2 // 如果在 Node 的 REPL 环境,可以写成 global.a
3 // 或者采用通用方法,写成 this.a
4 window.a // 1
5
6 let b = 1;
7 window.b // undefined
8
9 //有var声明的全局变量a 也属于顶层对象的属性, 因此window下有a属性,
10 //全局变量b有let命令声明,所以他不是顶层对象属性, 返回undefined