第六章 避免使用全局变量
JavaScript执行环境在很多方面都有其独特之处,全局变量就是其中之一。“全局变量”是一个神秘的对象,它表示了脚本的最外层上下文。
在浏览器中,windows对象往往重载并等同于全局对象,因此任何在全局作用域声明的变量和函数都是windows对象的属性。
6.1 全局变量带来的问题
这个就不用照着书详谈了,当我们进入团队合作编写代码时,若大家自定义的变量都是直接挂载在windows对象上(也就是全局变量),很容易发生命名冲突。像这样:
function sayColor() {
alert(color); // 不好的做法: color是从哪来的?
}
so, 尽量避免定义全局变量
如果这样写函数,也是有问题的,这是一个依赖于全局变量的函数。如果环境发生变化,函数很可能就失效了。
如果color被当做参数传入,代码可维护性会变得更佳。
function sayColor(color) {
alert(color);
}
so, 定义函数的时候,最好尽可能多的将数据置于局部作用域内,在函数内定义的任何“东西”都应当采用这种做法,任何来自函数外部的数据都应当以参数的形式传进来。
6.2 意外的全局变量
JS有个特性大家应该都懂: 就是没有使用var声明的变量会被当做全局变量。
这个特性导致了有些人用一个单var语句声明俩个变量,结果在第一个变量后不小心敲了分号而不是逗号从而把第二个变量自动创建成了全局变量。(不过我的IDE都提示了)
function doSomething() {
var count = 10;
title = "Maintainable JavaScript"; // 不好的写法:创建了全局变量
}
so, 这个问题解决的方法有很多,依靠现代的许多智能IDE(如:WebStorm之类的)或者JSLine和JSHint工具也可以检查出来并警告,或者直接将代码采用严格模式。
"use strict";
foo = 10; // 引用错误: foo未被定义
6.3 单全局变量方式
为了解决前面的问题,最佳的办法就是依赖尽可能少的全局变量,即只创建一个全局变量。
单全局变量模式已经在各种流行的JavaScript类库中广泛使用了。
- YUI定义了唯一一个YUI全局对象。
- jQuery定义了俩个全局对象,$和jQuery。只有在$被其他的类库使用了的情况下:为了避免冲突,应当使用jQuery。
- Dojo定义了一个dojo全局对象。
“单全局变量”的意思是所创建的这个唯一全局对象名是独一无二的(不会和内置的API产生冲突),并将你所有的功能代码都挂载到这个全局对象上。
因此每个可能的全局变量都成为你唯一全局变量的属性。从而不会创建多个全局变量。
// 这段代码创建了4个全局对象:Book、Chapter1、Chapter2和Chapter3.
function Book(title) {
this.title = title;
this.page = 1;
}
Book.prototype.turnPage = function(direction) {
this.page += direction;
}
var Chapter1 = new Book("Introduction to Style Guidelines");
var Chapter2 = new Book("Bssic Formatting");
var Chapter3 = new Book("Comments");
单全局变量则只会创建一个全局对象并将这些对象都赋值为它的属性。
var MaintainableJS = {}; MaintainableJS.Book = function(title) {
this.title = title;
this.page = 1;
} MaintainableJS.Book.prototype.turnPage = function(direction) {
this.page += direction;
}
MaintainableJS.Chapter1 = new MaintainableJS.Book("Introduction ti Style Guidelines");
MaintainableJS.Chapter2 = new MaintainableJS.Book("Basic Formatting");
MaintainableJS.Chapter3 = new MaintainableJS.Book("Comments");
这段代码已有一个全局对象,即 MaintainableJS, 其他任何信息都挂载到这个对象上。因为团队中的每个人都知道这个全局对象,因此很容易做到继续为它添加属性以避免全局污染。
作者还介绍了关于命名空间(namespace)和模块(modules)的概念。
命名空间我这里就不说了。。因为这个概念就是很老的YUI框架运用的概念。现在的框架大多数是模块化的,所以我重点做模块的总结。
6.3.2 模块
模块这一节包含了大量我听过但不没有去弄懂的知识。
首先介绍模块:另外一种基于单全局变量的扩充方法是使用模块,模块是一种通用的功能片段,他并没用创建新的全局变量或命名空间。
相反,所有的这些代码都存放于一个表示执行一个任务或发布一个接口的单函数中。可以用一个名称来表示这个模块,同样这个模块可以依赖其他模块。
因为JavaScript本身不包含正式的模块概念,自然也没有模块语法(期待ECMAScript6), 目前,世界上,有两种比较流行的 JavaScript 模块化体系,
一个是 Node 实现的 CommonJS,另外一个是 AMD。
对于这俩者:了解他们也算是了解前端的历史了。。我在这里只将 看过的大神的解释 抛出,因为这确实不是一篇帖子就能说明的。
首先是了解CMD,它是前端界的名人玉伯在写完SeaJS后的推广过程中对模块定义的规范化产出。(这是一种思想- -SeaJS已经没落了)
接下来是AMD,它是 RequireJS 在推广过程中对模块定义的规范化产出。
他本人在知乎上的回答就概括了俩个规范:点这里
另一个是前端界的名人阮一峰对这俩个规范的解释:点这里
6.4 零全局变量
完全独立的脚本才会用到这种情形(注意:完全无依赖)。
(function() { "use strict"; var doc = win.document; // 在这里定义其他的变量
// 其他相关代码 }(Window));