正文
大多数的主流语言都是有块级作用域的,变量在最近的代码块中,Objective-C 和 Swift 都是块级作用域的。但是在 JavaScript 中的变量是函数级作用域的。不过在最新的 ES6 中加入了 let 和 const 关键字以后,就变相支持了块级作用域。到了 ES6 以后支持块级作用域的有以下几个:
with 语句
用 with 从对象中创建出的作用域仅在 with 声明中而非外 部作用域中有效。
try/catch 语句
JavaScript 的 ES3 规范中规定 try/catch 的 catch 分句会创建一个块作用域,其中声明的变量仅在 catch 内部有效。
let 关键字
let关键字可以将变量绑定到所在的任意作用域中(通常是{ .. }内部)。换句话说,let 为其声明的变量隐式地了所在的块作用域。
const 关键字
除了 let 以外,ES6 还引入了 const,同样可以用来创建块作用域变量,但其值是固定的 (常量)。之后任何试图修改值的操作都会引起错误。
这里就需要注意变量和函数提升的问题了,这个问题在前一篇文章里面详细的说过了,这里不再赘述了。
不过这里还有一个坑,如果赋值给了一个未定义的变量,会产生一个全局变量。
在非严格模式下,不通过 var 关键字直接给一个变量赋值,会产生一个全局的变量
function func() { x = 123; }
func();
x
<123
不过在严格模式下,这里会直接报错。
function func() { 'use strict'; x = 123; }
func();
在 ES5 中,经常会通过引入一个新的作用域来限制变量的生命周期,通过 IIFE(Immediately-invoked function expression,立即执行的函数表达式)来引入新的作用域。
通过 IIFE ,我们可以
-
避免全局变量,隐藏全局作用域的变量。
-
创建新的环境,避免共享。
-
保持全局的数据对于构造器的数据相对独立。
-
将全局的数据附加到单例对象上。
-
将全局数据附加到方法中。
(1). with 语句
with 语句被很多人都认为是 JavaScript 里面的糟粕( Bad Parts )。起初它被设计出来的目的是好的,但是它导致的问题多于它解决的问题。
with 起初设计出来是为了避免冗余的对象调用。
举个例子:
foo.a.b.c = 888;
foo.a.b.d = 'halfrost';
这时候用 with 语句就可以缩短调用:
with (foo.a.b) {
c = 888;
d = 'halfrost';
}
但是这种特性却带来了很多问题:
function myLog( errorMsg , parameters) {
with (parameters) {
console.log('errorMsg:' + errorMsg);
}
}
myLog('error',{});
myLog('error',{ errorMsg:'stackoverflow' });
可以看到输出就出现问题了,由于 with 语句,覆盖掉了第一个入参。通过阅读代码,有时候是不能分辨出这些问题,它也会随着程序的运行,导致发生不多的变化,这种对未来的不确定性就很容易出现
bug。
with 会导致3个问题:
变量查找会变慢,因为对象是临时性的插入到作用域链中的。
@Brendan Eich 解释,废弃 with 的根本原因不是因为性能问题,原因是因为“with 可能会违背当前的代码上下文,使得程序的解析(例如安全性)变得困难而繁琐”。
所以在严格模式下,已经严格禁止使用 with 语句。
Uncaught SyntaxError: Strict mode code may not include a with statement
如果还是想避免使用 with 语句,有两种方法:
-
用一个临时变量替代传进 with 语句的对象。
-
如果不想引入临时变量,可以使用 IIFE 。
(function () {
var a = foo.a.b;
console.log('Hello' + a.c + a.d);
}());
或者
(function (bar) {
console.log('Hello' + bar.c + bar.d);
}(foo.a.b));
(2). eval 函数
eval 函数传递一个字符串给 JavaScript 编译器,并且执行其结果。