JS作用域浅析

我们都知道,JS的作用域其实包含了一系列的气泡,这些气泡包含了标识符(函数和变量)的定义,而这些气泡相互嵌套并且整齐排列。而在JavaScript中,这种气泡指的是函数作用域块级作用域

函数作用域

函数作用域是JS中最基本也是最常见的一种作用域。所谓函数作用域,指的是在函数声明的过程中产生的一个“气泡”,这个“气泡”可以包含标识符。

来看一个例子:

1
2
3
4
5
6
7
8
9
function foo(){
var a = 1;
function bar(){
var b = 2;
}
}
console.log(a); // 失败
console.log(b); // 失败
bar(); //失败

在上面的这段代码中,函数foo所形成的作用域包含了标识符变量a以及函数bar,而函数bar所形成的作用域包含了变量b。当然,全局作用域中也包含标识符函数foo

当然,就像上面程序的运行结果一样,直接访问变量a,b,函数bar都将失败。因为在函数作用域中,其声明的变量和函数中能在其内部(包含嵌套的作用域)使用。

由于函数作用域的特性,将会带来很多优点,譬如函数作用域可以隐藏函数内部的实现(非常重要),也可以避免变量在声明过程中产生的冲突以及覆盖。

块级作用域

有Java学习经验的童鞋对块级作用域可谓是了解,然而在JS中,块级作用域可不是那么常见(至少是在ES6出现以前)。

在let和const关键字出现以前,如果想找到块作用域的影子,那么只有with和try…catch语句了。

with关键字是JS块作用域的一个典型,在该作用域的范围内声明的变量都只在with语句块中有效。

而块作用域的另一个应用则是在try…catch中,相信即使对JS不够了解的童鞋都知道异常处理,对于JS中的try…catch语句,在catch块中将会产生一个err对象,而这一个对象只能在catch块中才能使用。看下面一个例子:

1
2
3
4
5
6
7
8
9
10
11
function showName(){
console.log(name);
}
try {
showName()
} catch (e) {
console.log(e);
} finally {
console.log('end');
}
console.log(e);

上面的这个例子showName函数内部试图打印一个并不存在的变量name,这里将使用一个RHS查找,并且在失败后抛出一个引用错误,我们可以在catch捕获到这个错误对象,但是我们没法在全局作用域上使用这个错误对象。

好了,除了这两个使用块作用域的典型,在ES6标准中,还新增了let和const关键字来实现块作用域。这里简单介绍一下,对ES6感兴趣的的童鞋们可以点这里来进一步了解ES6。

let和const都是有别于var的另外两种声明方式,let用于声明变量,该变量将会被绑定在{…}中,也就是说使用let声明的变量具有块级作用域。使用let声明的变量不但具有块级作用域,同时变量也不会提升。而const则用于声明常量,同样具有块级作用域,并且也不存在提升。let可以很好的用于循环,防止变量对于环境的污染。

小结

函数是JS中最常见的作用域,声明在函数内部的变量和函数将会被很好的隐藏起来,这是一种良好的设计原则。而在ES6中,块级作用域再次被人们所日常使用。块作用域到底是不是函数作用域的替代方案,我认为到目前为止,不是!我们应该自己选择使用何种作用域,如何结合使用这两种作用域,来创造更加可读和健壮的程序。

分享到 评论