1. 程式人生 > 實用技巧 >前端開發學習指南2

前端開發學習指南2

總是用大括號

// 不好
if (foo)
    bar();

// 好 :)
if (foo) {
    bar();
}

字串處理

引用字串永遠要用雙引號。 有些人非常喜歡用C語言風格的字串(單引號),但這種習慣會導致指令碼內部的風格衝突。C語言風格的字串處理要求空字串和單字元包在單引號裡,而短語和單詞必須包在雙引號內。

註釋

往程式碼裡玩命加註釋的需求是由各種經理、主管及其他很少接觸程式碼的人們引領的。這種需求無非是僱員們考核指標中的一個勾選欄,花在這上面的時間基本沒有帶來什麼回報。 如果那些從善如流的開發者能遵循本文件中提出的建議,他們的程式碼會變得相當易於閱讀,一目瞭然,以至於再用註釋描述這些程式碼會多餘得令人尷尬。來看下面的例子。在這裡,布林變數被作為問題提出,而函式也有直觀的命名。

if (user.hasPermission) {
    editPage();
}

至少在這個場景中,註釋是完全沒有必要的。

註釋重要的情況

一個專案裡,永遠會有某些部分難以查閱和理解。比如一個複雜的正則表示式,或者一個對角度進行計算或在度和弧度單位之間切換的數學函式。沒有上面的註釋,初級或中級的讀者將對指令碼的含義毫無頭緒。

// 校驗美國電話號碼的正則表示式,號碼格式是 (XXX) XXX-XXXX (減號、空格和括號都是可選的,可以有也可以沒有)
var phoneRegEx = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;

總是使用 === 比較符

使用 == 比較符可以讓令人鬱悶的bug消失於無形。它允許在JavaScript花園中有清楚解釋的弱型別。使用嚴格的 === 比較符不會執行型別強制轉換,從而能夠嚴格地評估兩個物件之間的差別。再說一遍,更多詳細資訊請參見JavaScript花園

var zeroAsAString = "0";

if (zeroAsAString == 0) {
    // 這樣也能判斷為true,呵呵...
}

if (zeroAsAString === 0) {
    // 判斷為false
}

例外

在和null進行比較的時候,允許使用 == 比較符,因為它會檢測null和undefined兩個屬性。如果你不完全理解這個原理,那我還是建議你用 === 比較符為好。

var foo = null;

// foo 是 null, 但 bar 是 undefined ,因為它尚未被宣告
if (foo == null && bar == null) {
    // 上面的判斷還是成立的
}

使用 .parseInt() 的時候,總是指定第二個 'radix' 引數

把字串解析為整數的時候,有個好習慣是指定第二個基數引數 -- 它會確定該字串被轉換成幾進位制。當字串以0開頭的時候,預設情況下會觸發16進製作為基數。大部分初級和中級使用者只會用到10這個基數。 感謝 João Moreno 記錄的這個勘誤

alert( parseInt("08") ); // alerts: 2

alert( parseInt("08", 10) ); // alerts: 8

避免比較 true 和 false

直接比較 true 和 false 的值是沒有必要的。有時候也許明確一下有好處,但它還是額外的程式碼。

if (foo === true) {
    // 用了 === 倒是不錯,可這是多餘的
}

if (foo) {
    // 贊!
}

if (!bar) {
    // 反過來也贊
}

避免汙染全域性名稱空間

過分依賴全域性變數是我們組所有人 -- 特別是我自己 -- 特別有負罪感的一件事。關於為啥全域性變數不好的討論是相當直接的:這增加了指令碼和變數衝突的概率,而且原始檔和名稱空間本身都會充斥著數不清的命名模糊的變數。

Douglas Crockford堅信一個Javascript應用的程式碼質量可以用其中使用的全域性變數數來評價,越少越好。由於並不是什麼都可以定義成local的(不過要誠實,其實你現在考慮的那個是可以的,別偷懶),你需要想辦法整理你的變數以避免衝突,並把名稱空間的膨脹減到最小。最簡單的方法就是採用單變數或者把用到這些全域性變數的模組儘可能減少。 Crockford提到YUI只用了一個全域性變數,YAHOO。他在他的博文"全域性統治"中討論了更多的細節問題。

考慮這種情況:對於小型web應用,全域性變數通常用於儲存應用級的設定,可以用你的專案名或者settings作為命名去定義一個物件,這樣總的來說會更好。

// 被汙染的全域性名稱空間
var settingA = true;
var settingB = false;
var settingC = "test";

// 用 settings 作為物件命名
var settings = {
    settingA: true,
    settingB: false,
    settingC: "test"
}

不過,如果我們可以通過避免使用全域性變數來減少衝突概率,但是把名稱空間標準化成一樣的,豈不是會增加各個應用之間產生衝突的概率麼?呃,這個擔憂確實有道理。所以,建議你用自己特定的應用名作為全域性變數的名稱空間,或者用和jQuery採取的$.noConflict()模式相同的方法重新分配你的名稱空間.

var myAppName = {
    settings: {
        settingA: true
    }
}

//訪問全域性變數
myAppName.settings.settingA; // true

駝峰法變數命名

JavaScript變數的駝峰法命名在大部分程式設計環境中都是作為標準的。有讀者在評論中提出了唯一的例外,就是要用大寫字母加下劃線來指代常量。

var X_Position = obj.scrollLeft;

var xPosition = obj.scrollLeft; // 更好,更簡潔

SCENE_GRAVITY = 1; // 常量

迴圈的效能 - 快取陣列長度

迴圈估計是Javascript效能調優最重要的部分了。在一個迴圈內部節省個一兩毫秒的,說不定總體就省出好幾秒來了。這裡有一招就是把陣列的長度快取,這樣在迴圈裡就無需每次迭代的時候都計算一遍了。

var toLoop = new Array(1000);

for (var i = 0; i < toLoop.length; i++) {
    // 敗家玩意 - 長度會反覆算 1000 次你知道不?
}

for (var i = 0, len = toLoop.length; i < len; i++) {
    // 會過日子 - 長度只計算一次,然後快取了
}

例外

如果你對一個數組做迴圈來查詢並刪除某個元素,這就會改變陣列長度。任何時候你只要會在迴圈內部增加或刪除元素來改變陣列的長度,你就給自己帶來了麻煩。這種情況下,你要麼每次改變後重新設定陣列長度,要麼就別快取它了。

迴圈的效能 - 使用 'break;' 和 'continue;'

跳過和跳出迴圈的能力對於避免開銷很大的迴圈週期是非常有用的。

如果你是在迴圈內部查詢,查詢成功以後你會做什麼?比如1000個元素的迴圈執行到一半,你就找到了你要的東西。你不管三七二十一,即使知道後面的if語句不會再有符合的機會,還是讓迴圈繼續把剩下的500個元素迭代完麼?不!你應該跳出迴圈,必須的!

var bigArray = new Array(1000);

for (var i = 0, len = bigArray.length; i < len; i++) {
    if (i === 500) {
        break;
    }
    console.log(i); // 這樣只會輸出 0 - 499
}

另一個問題是跳過某個特定的迭代,然後繼續迴圈。雖然說類似於奇偶數這樣的條件可以通過把i++替換成i + 2的辦法來管理,有些條件還是需要具體檢測,然後觸發跳過操作。任何能夠避免執行完整的迭代過程的東西都是很有用的。

var bigArray = new Array(1000);

for (var i = 0, len = bigArray.length; i < len; i++) {
    if (condition) {
        continue;
    }
    doCostlyStuff();
}

函式呼叫不要傳輸太多的引數

對於可讀性而不是其他因素來說,下面這種方式真是糟透了:

function greet(name, language, age, gender, hairColour, eyeColour) {
    alert(name);
}

下面的例子預先構建了一個物件作為引數,或者把內聯物件傳遞過去,這樣就好多了。

function greet(user) {
    alert(user.name);
}

greet({
    name: "Bob",
    gender: "male"
});

把 'this' 對映為 'self'

在編寫面向物件(OO)Javascript程式碼的時候, 必須瞭解this的作用範圍. 不管你用來構建偽類的設計模式是什麼,對this的引用是指向一個例項的最簡單辦法。當你開始把jQuery的helper方法和你的偽類整合的時候,你就會注意到this的作用範圍變化。

Bob.findFriend("Barry");

Person.prototype.findFriend = function(toFind) {
    // this = Bob
    $(this.friends).each(function() {
        // this = Bob.friends[i]
        if (this.name === toFind) {
            // this = Barry
            return this;
        }
    });
}

在上面的例子裡,this經歷了從對Bob的引用,變成對他的朋友Barry的引用的過程。 瞭解this的取值在一段時間發生的變化是很重要的。在原型函式內部,this指向所在偽類的當前例項(這裡是Bob)。而一旦我們進入$.each()迴圈,this就會重新對映為被解析陣列的第i個元素。

解決辦法是把this的值重新對映為self或者_self。雖然self(不帶下劃線)並不是保留字, 但它確實是window物件的一個屬性。雖然我上面用到self的例子是從jQuery原始碼中挑的,但他們也認識到了這個錯誤,正打算修正目前的狀況,也就是改用_self。我個人還是喜歡用self,不為別的,就因為它的簡潔 -- 不過它可能會冒出一些非常令人困惑的bug。總之,用self有風險,使用需謹慎。

在下面的例子中,我會更好地利用在$.each()helper 中提供的引數,同時重新對映this的值。

Bob.findFriend("Barry");

Person.prototype.findFriend = function(toFind) {

    // 就這一次用到了 "this"
    var _self = this;

    $(_self.friends).each(function(i,item) {
        if (item.name === toFind) {
            return item;
        }
    });

}

我能用 Boolean 嗎?

布林變數必須能夠很容易通過命名來識別。可以用類似於is,can或者has的字首來形成一個問句。

isEditing = true;

obj.canEdit = true;

user.hasPermission = true;

儘量減少重新繪製和重新佈局

重新繪製和重新佈局與重新渲染DOM的過程關聯,這個過程會在特定屬性或元素被改變時發生。重新繪製是在某個元素的外觀被改變但沒有對佈局進行調整的情況下觸發的。 Nicole Sullivan 在一篇全面的博文中把這些改變描述為諸如是否可見或背景色變化之類的樣式改變。重新佈局則是開銷更大的操作,由調整頁面佈局的一些改變引發。例如增加或刪除元素,改變某個元素的寬度或高度,甚至是改變瀏覽器視窗的大小。最糟糕的情況是重新佈局導致先輩、兄弟和孩子節點元素也需要重新佈局的多米諾骨牌效應。

毫無疑問,重新繪製和重新佈局應該儘量避免,但是如何做到呢?

重新佈局的例子

其實也不是說下面的程式碼就很糟糕啦。不過我們先假定陣列arr有10個元素