JavaScript 通过函数管理作用域。在函数内部声明的变量只在这个函数内部可用,而在函数外面不可用。另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。
每个 JavaScript 环境有一个全局对象,当你在任意的函数外面使用 this 的时候可以访问到。你创建的每一个全局变量都成了这个全局对象的属性。在浏览器中,方便起见,该全局对象有个附加属性叫做 window,此 window(通常)指向该全局对象本身。下面的代码片段显示了如何在浏览器环境中创建和访问的全局变量:
myglobal = 'nowamagic' // 不推荐写法
console.log(myglobal) // "hello"
console.log(window.myglobal) // "hello"
console.log(window['myglobal']) // "hello"
console.log(this.myglobal) // "hello"
全局变量的问题在于,你的 JavaScript 应用程序和 web 页面上的所有代码都共享了这些全局变量,他们住在同一个全局命名空间,所以当程序的两个不同部分定义同名但不同作用的全局变量的时候,命名冲突在所难免。
web 页面包含不是该页面开发者所写的代码也是比较常见的,例如:
- 第三方的 JavaScript 库
- 广告方的脚本代码
- 第三方用户跟踪和分析脚本代码
- 不同类型的小组件,标志和按钮
比方说,该第三方脚本定义了一个全局变量,叫做 result;接着,在你的函数中也定义一个名为 result 的全局变量。其结果就是后面的变量覆盖前面的,第三方脚本就一下子嗝屁啦!
因此,要想和其他脚本成为好邻居的话,尽可能少的使用全局变量是很重要的。一些减少全局变量的策略,例如命名空间模式或是函数立即自动执行,但是要想让全局变量少最重要的还是始终使用 var 来声明变量。
由于 JavaScript 的两个特征,不自觉地创建出全局变量是出乎意料的容易。首先,你可以甚至不需要声明就可以使用变量;第二,JavaScript 有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性。参考下面的代码:
function sum(x, y) {
// 不推荐写法: 隐式全局变量
result = x + y
return result
}
此段代码中的 result 没有声明。代码照样运作正常,但在调用函数后你最后的结果就多一个全局命名空间,这可以是一个问题的根源。
经验法则是始终使用 var 声明变量,正如改进版的 sum() 函数所演示的:
function sum(x, y) {
var result = x + y
return result
}
另一个创建隐式全局变量的反例就是使用任务链进行部分 var 声明。下面的片段中,a 是本地变量但是 b 确实全局变量,这可能不是你希望发生的:
// 反例,勿使用
function foo() {
var a = (b = 0)
// ...
}
此现象发生的原因在于这个从右到左的赋值,首先,是赋值表达式 b = 0,此情况下 b 是未声明的。这个表达式的返回值是 0,然后这个 0 就分配给了通过 var 定义的这个局部变量 a。换句话说,就好比你输入了:
var a = (b = 0)
如果你已经准备好声明变量,使用链分配是比较好的做法,不会产生任何意料之外的全局变量,如:
function foo() {
var a, b
// ... a = b = 0; // 两个均局部变量
}
然而,另外一个避免全局变量的原因是可移植性。如果你想你的代码在不同的环境下(主机下)运行,使用全局变量如履薄冰,因为你会无意中覆盖你最初环境下不存在的主机对象(所以你原以为名称可以放心大胆地使用,实际上对于有些情况并不适用)。