以前我基于谷歌地图封装过一个很大型的船舶监控的JS插件。当时由于入行时间不够,加之经验不足,导致js写得不好,全局变量到处都是。到后面居然前面的会覆盖前面的全局变量。今天就来研究下使用太多全局变量导致的问题。
一、全局变量是如何产生的
我们知道全局变量都是挂载在window对象上面的。它的产生方式有很多种,下面就列出来。
1、人为定义的
人为定义很简单,你这样写的代码就定义了一个全局变量person:
1 2 3 |
|
2、漏写var
很多人以为在函数内部写的变量只是函数内有效。实际上javascript并不是这样。如果在函数内定义变量的时候没写var,那么它依然是全局变量。
1 2 3 4 5 6 7 |
|
注意到,调用了一次name之后,就创建了name的全局变量,到处都可以访问。
只有在严格模式下,浏览器才会提示错误:name未定义;
有一个消防法,使用JSLint或JSHint扫描代码能够理清楚这些无意创建的全局变量。
二、全局变量太多会带来哪些问题
当然,少量的全局变量不会造成太大影响。但是如果js代码庞大了之后,到处都是全局变量会造成什么问题呢?
1、命名冲突
全局变量太多时,可能我们无意之中声明的一个全局变量,其实之前已经存在。这是可能就会造成后面的值覆盖掉前面的值。
2、代码脆弱
比如,在函数内部依赖一个全局变量,一旦这个全局变量被删除或被修改,都会影响到这个函数的执行是否正确。
1 2 3 4 5 6 7 8 |
|
如,对于上面两个函数的写法,依赖参数存入要好于依赖全局变量。
3、难以测试
依赖全局变量之后,整个框架要依赖于全局变量才能运行。所以要想进行局部测试或单元测试就必须要创建好完整的全局环境。
三、减少全局变量的方法
减少全局变量的方法有好多,下面就推荐几种。
1、单全局变量
单全局变量的意思是,只创建一个全局变量。然后其他的全局变量作为属性挂载到这个全局变量上面。
比如:
- jQuery定义了两个全局对象:$和jQuery。
- YUI定义了一个唯一的全局对象YUI全局对象。
- Dojo定义了一个dojo的全局对象。
比如,我想需要实现如下逻辑:
1 2 3 4 5 6 7 8 9 10 |
|
以上代码逻辑创建了1个全局函数,2个全局对象:Person、p1、p2。
实际上,完全可以只创建一个对象就能完成同样的功能。
1 2 3 4 5 6 7 8 9 10 11 |
|
以上代码,将所有的全局函数与全局对象都挂载到myjs这个全局对象上了。
实际上,你还可以挂载更多的变量、对象、函数等等。
2、命名空间
基于单全局变量,当变量太多时,只有一级也可能会造成同名覆盖的问题。这时候,我们就命名空间的方式来创建多级别的变量挂载。
命名空间这个概念在javascript中是不存在的,实际上是巧妙利用javascript的代码特性实现的功能划分。
雅虎的YUI就是依照命名空间的思路来管理它的代码。
比如Y.DOM下的所有方法都是与DOM操作相关的,Y.Event下的所有方法都是和事件相关的。
在javascript中,我们可以轻松地使用对象来创建自己的命名空间。
如,假设基于地图封装了一个船舶监控系统:
1 2 3 |
|
对于这种命名空间的方式,要特别注意的是,使用之前,要判断命名空间是否存在。
例如:
1 2 3 |
|
使用MyGlobal.Person.Say这个命名空间,当MyGlobal下不存在Person这个对象,就会报错。这个可以为MyGlobal写一个函数,当没有这个命名空间时逐级创建,如果这个命名空间已存在则直接返回。这样就可以省略掉使用命名空间要判断的问题了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
在想使用任意命名空间前,只需要调用一下:MyGlobal.namespace("Person.Say");
3、模块化
这个东西得长篇大论,要独立一篇文章来写。
4、零全局变量(闭包)
使用闭包的场景不多,当我需要一段JS,并且这段JS不需要被其他JS访问,只是为了实现单一的功能而创建的。向轻松学会闭包的可以查看这篇文章《javascript闭包》。
最简单的闭包:
1 2 3 4 |
|
你可以在"自己的代码逻辑",写上你自己的逻辑。这种方式有如下两个要求:
- 代码不需要被其他代码所依赖;
- 代码不需要被经常扩展;
即,脚本非常短,且不需要与其他的JS进行交互,才可以使用这种闭包的方式。实际上它真正用上的场景不多的。