首页 > 代码库 > javascript 减少全局变量的方法和好处

javascript 减少全局变量的方法和好处

以前我基于谷歌地图封装过一个很大型的船舶监控的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进行交互,才可以使用这种闭包的方式。实际上它真正用上的场景不多的。

出处:javascript 减少全局变量的方法和好处

javascript 减少全局变量的方法和好处