編寫可維護的JavaScript——全局變量

JavaScript執(zhí)行環(huán)境在很多方面都有其獨特之處。全局變量和函數(shù)的使用便是其中之一。事實上,JavaScript的初始執(zhí)行環(huán)境是由多種多樣的全局變量所定義的,這些全局變量在腳本環(huán)境創(chuàng)建之初就已經(jīng)存在了。這些都是掛載在全局對象上的。

在瀏覽器中,window對象往往重載并等同于全局對象,因此任何在全局作用域中聲明的變量和函數(shù)都是window對象的屬性,如下所示,兩者都是window對象的屬性。

var color = 'red';
 
function showColor () {
    alert(color);
}

console.log(window.color);    // 'red'
console.log(typeof window.showColor);    // 'function'

1. 全局變量帶來的問題

一般來講,創(chuàng)建全局變量被認為是糟糕的事,尤其是在團隊開發(fā)的大背景下更是如此。

命名沖突: 當腳本中的全局變量和全局函數(shù)越來越多時,發(fā)生命名沖突的概率也大為增加。如上代碼所示,當全局變量color和全局函數(shù)showColor()在同一個文件時還好,但是當有很多地方引用全局變量和全局對象時,追蹤起來就變得相當麻煩。

代碼脆弱: 一個依賴于全局變量的函數(shù)即是深耦合于上下文環(huán)境之中,如果環(huán)境發(fā)生改變,函數(shù)很可能就失效了。

意外的全局變量: 當你給一個未被var語句聲明過的變量賦值時,JavaScript就會自動創(chuàng)建一個全局變量,尤其當你無意中創(chuàng)建的全局變量與系統(tǒng)定義的全局變量相同時,會修改系統(tǒng)的全局變量的值。如下:

function doSomething () {
    var count = 10;
        title = "Maintainable JavaScript";    //  不好的寫法,創(chuàng)建了全局變量
}
2. 解決全局變量帶來的問題
a. 避免意外的全局變量

避免意外的全局變量可以使用JSLint和JSHint等檢測工具來,或者使用JS的嚴格模式,使用JS的嚴格模式會通知JavaScript引擎在運行代碼前執(zhí)行更嚴格的錯誤處理和語法檢查。其中一個規(guī)則可以探測未聲明變量的賦值操作。

b. 單全局變量

單全局變量的含義即只創(chuàng)建一個全局變量。如JQuery定義了兩個特定的全局變量,$和jQuery,只有在$被其他類庫使用了的情況下,為了避免沖突,應當使用jQuery。

“單全局變量” 的意思是所創(chuàng)建的這個唯一全局對象名是獨一無二的,并將你所有的功能代碼都掛載到這個全局對象上。因此每個可能的全局變量都成為你唯一全局對象的屬性,從而不會創(chuàng)建多個全局變量。

c. 命名空間

即使你的代碼只有一個全局對象,也存在著全局污染的可能性。大多數(shù)使用單全局變量模式的項目同樣包含“命名空間”的概念。命名空間是簡單的通過全局對象的單一屬性表示的功能分組。將功能按照命名空間進行分組,可以讓你的全局對象變得井然有序,同時讓團隊成員能夠知曉新功能應該屬于哪個部分。

現(xiàn)有js中并不支持原生的命名空間。在JS中創(chuàng)建的任何對象都默認是全局對象。在現(xiàn)代的大規(guī)模JS開發(fā)中,不采用命名空間會造成非常糟糕的命名方式,導致代碼丑陋不可讀。當引入第三方庫后,更可能會發(fā)生明明覆蓋的情況。

好消失是:在ES6中,就有了native的命名空間可以用了,但是當下我們還需要一些特殊的手段來模擬命名空間的概念。簡單來說,就是創(chuàng)建一個簡單字面量來打包所有的相關函數(shù)和變量。這個簡單的對象字面量模擬了命名空間的作用。

var NAMESPACE = {
    person: funnction(name) {
        this.name = name;
        this.getName = function() {
            return this.name;
        }
    }
}

// 調(diào)用方法
var p = new NAMESPACE.person("andy");
p.getName()

如此一來,我們就可以通過命名空間來聲明多個person對象了。但是這里還有一個問題,我們這里使用的是一個全局對象,在添加這個“命名空間”的時候,我們有可能覆蓋全局空間中的同名對象。因此我們需要再聲明命名空間之前進行檢查,保證全局空間的安全:

// 在聲明之前進行檢查,防止覆蓋全局的同名對象。
var NAMESPACE = NAMESPACE || {};

若全局空間中已經(jīng)有同名對象,則不覆蓋該對象;否則創(chuàng)建一個新的命名空間。采用了這個安全地命名空間后,聲明的方法也需要略作改動:

var NAMESPACE = NAMESPACE || {};

MYNAMESPACE.person = function(name) {
    this.name = name;
};

MYNAMESPACE.person.prototype.getName = function() {
    return this.name;
};

// 使用方法
var p = new MYNAMESPACE.person("ifcode");
p.getName();        // ifcode

注意在定義命名空間構造函數(shù)時,需要將其定義在prototype上,否則新建的實例無法訪問對象的方法。

d. 模塊化

另外一種基于單全局變量的擴充方法是模塊,模塊是一種通用的功能片段,它并沒有創(chuàng)建新的全局變量或命名空間。相反,這些代碼都存放于一個表示執(zhí)行一個任務或發(fā)布一個借口的單函數(shù)中??梢杂靡粋€名稱來表示這個模塊,同樣這個模塊可以依賴其他模塊。

e. 零全局變量

這個種方法應用場景不多,只有在特殊場景下才會應用。最常見的情形是一段不會被其他腳本訪問到的完全獨立的腳本,之所以存在這種情形,是因為所有所需的腳本都會合并到一個文件,或者因為這段非常短小且不提供任何借口的代碼會被插入至一個頁面中,最常見的用法是創(chuàng)建一個書簽。

書簽是獨立的,他們并不知道頁面中包含什么且不需要頁面知道它的存在。最終我們需要一段“零全局變量”的腳本嵌入到頁面中,實現(xiàn)方法就是使用一個立即執(zhí)行的函數(shù)調(diào)用并將所有腳本放置其中,比如“

(function (win)) {
    var doc = win.document;
    
    // 在這里定義其他的變量

    // 其他相關代碼
}

這段代碼注入到頁面中不會產(chǎn)生任何的全局變量,之后你可以通過將函數(shù)設置為嚴格模式來避免創(chuàng)建全局變量。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 一、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運行的地址不確定 關于...
    SeanCST閱讀 8,146評論 0 27
  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,619評論 0 13
  • 第3章 基本概念 3.1 語法 3.2 關鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,547評論 0 21
  • 打算開始寫每日總結,內(nèi)容包括今天看過的信息、發(fā)生的事、吃喝生活、工作學習、人人人感悟。 原因一 復盤生活 正在...
    midtwo閱讀 262評論 0 0
  • 全文總目錄 彭磊兩手搓著,有些難為情地說,“我真是擔心自己管不好,萬一把公司給整垮了,我怎么對得住你呢。再說了,我...
    簡小塵閱讀 357評論 1 4

友情鏈接更多精彩內(nèi)容