作用域
作者提到了 ES5 中有三种方式实现作用域:function、with 和鲜为人知的 try/catch 。with 已经被淘汰,function 方式可以看看之前翻译的经典文章《深入理解 JavaScript 模块模式 》,而最后一种 hack 真是让人眼前一亮。他在附录 B 中也提到 google 的 Traceur 也是这么实现的,我试了一下,发现 Traceur 与 Babel 现在都没有采用这种方式了,而是直接检测 shadow 冲突再使用不同的变量名,这可能是考虑到性能的问题。
1 |
// ES6 代码 |
TDZ
有意思的是,这几种 hack 都不能解决 let
变量不许提升 (hoist) 的问题。
1 |
console.log(a); // ReferenceError |
let
声明行到它所在 block 最开始之间的区域被称为 TDZ “Temporal dead zone”,正常情况下,可以考虑用回上面的方法 hack ,把 TDZ 当做一个
block,但是这里提到了一种坑爹的情况,函数。
1 |
function foo() { |
1 |
function foo() { |
1 |
foo(); |
这么一来静态分析出错误就变得很麻烦,在 Traceur 的一个 issue 中 @arv 提到了牺牲运行时来检测的方式:
1 |
// ES6 |
这在 Babel 中需要开启 es6.blockScopingTDZ
特性:
1 |
$ babel-node --optional es6.blockScopingTDZ test.js |
(3 月 19 日 补)
注意 for
循环闭包的坑,现在还没有好的 polyfill 方案
1 |
// 0 1 2 3 4 |
块级作用域
作者提到几个块级作用域的好处,比较有意思的一个是对垃圾回收的优化:
1 |
function process(data) { |
这里 btn
的回调函数虽然没有直接用到 someReallyBigData
,但 JS 引擎很可能会继续保留 someReallyBigData
因为闭包使回调函数引用了 someReallyBigData
所在的作用域。ES6 中,使用显式的块则可以向引擎明确回收时机:
1 |
function process(data) { |