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