避免使用全域變數
一般來講,創建全域變數被認為是最糟糕的實踐,尤其是在團隊開發的大背景下更是問題多多,隨著代碼量的增長,全域變數會導致一些非常重要的可維護性難題,全域變數越多,引入錯誤的概率也就越來越高,
命名沖突
當腳本中的全域變數和全域函式越來越多的時候,發生命名沖突的概率隨之增高,即很可能無意間使用了一個已經宣告的變數,多有的變數都被定義為區域變數,這樣代碼才是最容易維護的,
function sayColor() {
alert(color); // 不好的做法:color是哪來的?
}
讓我們更進一步,如果color在腳本的定義存在很多處,那么隨著sayColor()函式被包含于代碼的位置不同,其執行結果也不盡相同,
全域環境是用來定義JavaScript內置物件的地方,如果你給這個作用域添加上了自己的變數,接下來則會面臨讀取瀏覽器附帶的內置變數的風險, 比如,對于名字為color來說,絕對是一個不安全的全域變數,它只是一個普通名詞,并沒有任何的限定符,因此和瀏覽器未來的內置API或其他開發者的代碼產生沖突的可能性極高,
代碼的脆弱性
一個依賴于全域變數的函式即使深耦合于背景關系環境中,如果環境發生改變,函式很可能失效,上一個例子,如果color不存在,sayColor()方法將會報錯,意味著任何對全域環境修改都可能會造成某處代碼的出錯,同樣,任何函式也會不經意間修改全域變數,導致對全域變數值得依賴變得不穩定,在上一個例子中,如果color當做引數傳入,代碼可維護性變得更佳,
function sayColor(color) {
alert(color); // 不好的做法:color是哪來的?
}
修改后的這個函式不再依賴全域變數,因此任何對全域環境的修改不會影響到它,color是一個引數,唯一值得注意的是傳入的函式值是否合法
當定義函式的時候,最好盡可能多的將資料置于區域作用域中,在函式內定義的“任何東西”都應該采用這種寫法,任何來自函式外部的資料都應當以引數形式傳入進來,這樣做可以將函式與外部環境隔開,并且你的修改不會對程式其他部分造成影響,
難以測驗
確保你的函式不會對全域變數有依賴,這將增強你的代碼的可測驗性,當然,你的函式可能會依賴原生的JavaScript全域物件,比如Date,Array等,他們是全域環境的一環,是和JavaScript引擎有關的,你的函式總是會用帶到這些全域物件,總之,為了保證你的代碼具有最佳的可測驗性,不要讓函式對全域變數有依賴
可以依賴全域物件(Data,Array ...),但是盡量減少全域變數的依賴
意外的全域變數
JavaScript中有不少陷阱,其中一個就是不小心創建全域變數,當你給一個未被var陳述句宣告的變數賦值的時候,JavaScript就會自動創建一個全域變數,比如:
function doSomething() {
var count = 10;
title = "test"; // 不好的寫法,創建了全域變數
}
不小心省略var陳述句可能意味著在你不知情的情況下修改了某個已存在的全域變數,
function doSomething() {
var count = 10;
name = "test"; // 不好的寫法,創建了全域變數
}
name實際上是window的一個默認屬性,window.name屬性經常用于框架(frame)和iframe的場景中,當點擊鏈接時,可以通過指定打開鏈接的目標容器;來控制其在特定的框架或選項卡中顯示,不小心修改name會影響到站點的鏈接導航,
最好的規則就是總是使用var定義變數,哪怕是定義全域變數,這樣大大降低默寫場景省略var所導致錯誤的可能性
避免意外的全域變數
使用嚴格模式,上面那種為使用var宣告變數的方式會報錯
function doSomething() {
var count = 10;
name = "test"; // 參考錯誤:foo未被定義
}
單全域變數方式
單全域變數模式已經在各種流行的JavaScript類別庫中廣泛使用了
- YUI定義唯一一個YUI全域物件
- jQuery定義了兩個全域物件
$和jQuery - Dojo定義一個dojo全域物件
- Closure類別庫定義了一個goog全域物件
“單全域變數”的意思是所創建的這個唯一全域物件名是獨一無二的(不會和內置API產生沖突),并將你所有功能代碼都掛載到這個全域物件上
function Book(title) {
this.title = title;
this.page = 1;
}
Book.prototype.turnPage = function(direction) {
this.page += direction;
}
var Chapter1 = new Book("A");
var Chapter2 = new Book("B");
var Chapter3 = new Book("C");
這段代碼創建了四個全域物件:Book,Chapter1,Chapter2,Chapter3,單全域變數模式則只會創建一個全域物件并將這些物件都復制為它的屬性,
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("A");
MaintainableJS.Chapter2 = new MaintainableJS.Book("B");
MaintainableJS.Chapter3 = new MaintainableJS.Book("C");
這段代碼只有一個全域物件,即MaintainableJS,其他任何資訊都掛載在這個的物件上,因為團隊都知道這個全域物件因此容易做到給其添加屬性,以避免全域污染
命名空間
即使你的代碼只有一個全域物件,也存在全域污染的可能性,大多數使用全域變數模式的專案同樣包含“命名空間”的概念,命名空間是簡單的對通過全域物件單一屬性表示的功能性分組,比如,YUI就是依照命名空間的思路來管理代碼的,Y.DOM下的所有方法都是和DOM操作相關的,Y.Event下的所有方法都是和事件相關的,以此類推,
var ZakasBooks = {};
// 表示這本書的命名空間
ZakasBooks.MaintainableJavaScript = {};
// 表示另一本書的命名空間
ZakasBooks.HighPerformanceJavaScript = {};
一個常見的約定是每個檔案中都通過創建新的全域物件來宣告自己的命名空間,在這種情況下,上面的案例是夠用的,
同樣有另外一些場景,,誒個檔案都需要給命名空間掛載東西,這個時候你需要首先保證這個命名空間是已經存在的,這是全域物件非破壞性的處理命名空間的方式變得非常有用,
var YourGlobal = {
namespace : function(ns) {
var parts = ns.split("."),
object = this,
i, len;
for (i = 0, len = parts.length; i < len; i++) {
if(!object[parts[i]]) {
object[parts[i]] = {};
}
object = object[parts[i]];
}
return object;
}
}
變數YourGlobal實際上可以表示任意名字,最重要的部分在于namespace()方法,我們給這個方法傳入一個表示命名空間物件的字串,它非破壞性地創建一個命名空間,
/*
* 同時創建YourGlobal.Books和YourGlobal.Books.MaintainableJavaScript
* 因為之前沒有創建他們,因此每個都是全新創建的
*/
YourGlobal.namespace("Books.MaintainableJavaScript");
// 你現在可以使用該命名空間了
YourGlobal.Books.MaintainableJavaScript.author = "Nicholas C. Zakas";
/*
* 不會操作YourGlobal.Books本身,同時會給它添加HighPerformanceJavaScript
* 他會保持YourGlobal.Books.MaintainableJavaScript原封不動
*/
YourGlobal.namespace("Books.HighPerformanceJavaScript");
// 仍然是合法的參考
console.log(YourGlobal.Books.MaintainableJavaScript.author)
// 你同樣可以在方法呼叫之后立即給它添加新屬性
YourGlobal.namespace("Books").ANewBook = {};
模塊
另一種基于單全域變數的擴充方法是使用模塊,模塊是一種通用的功能片段,它并沒有創建新的全域變數或命名空間,相反,所有的這些代碼都存放于一個表示執行任務或發布一個介面的單函式中,可以用一個名稱來表示一個模塊,同樣這個模塊可以依賴其他模塊
零全域變數
你的JavaScript代碼注入到頁面時是可以做到不用創建變數的,這種方法應用場景不多,因此只有在某些特殊場景下才會有用,最常見的情形就是一段不會被其他腳本訪問到的完全獨立的腳本,之所以存在這樣的情形,是因為所有所需的腳本都會合并到一個檔案,或者因為這段非常短小且不提供任何借口的代碼會被插入至一個頁面中,最常見的用法是創建一個書簽
書簽是獨立的,他們并不知曉頁面內容包含什么且不需要頁面知道它的存在,最終我們需要一段“零全域變數”的腳本嵌入到頁面中,實作方法就是使用一個立即執行的函式呼叫并將所有腳本放置其中,比如:
(function(win) {
var doc = win.document;
// 在這定義其他變數
// 其他相關代碼
}(window));
這段立即執行的代碼傳入window物件,因此這段代碼不需要直接參考任何全域變數,在這個函式內部,變數doc是指向document物件的參考,只要是函式代碼中沒有直接修改window或doc且所有變數都是用var宣告,這段腳本則可以注入到頁面中而不會產生任何全域變數,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/240420.html
標籤:JavaScript
上一篇:第五章 UI層松耦合
