1. 按強類型風格寫代碼
JS 是弱類型的,但是寫代碼的時候不能太隨意,哈樂魚寫得太隨意也體現了編碼風格不好。下面分點說明:
(1)定義變量的時候要指明類型,告訴 JS 解釋器這個變量是什么數據類型的,而不要讓解釋器去猜,例如不好的寫法:
聲明了三個變量,但其實沒什么用,因為解釋器不知道它們是什么類型的,好的寫法應該是這樣的:
定義變量的時候就給他一個默認值,這樣不僅方便了解釋器,也方便了閱讀代碼的人,他會在心里有數——知道這些變量可能會當作什么用。
(2)不要隨意地改變變量的類型,例如下面代碼:
第 1 行它是一個整型,第 2 行它變成了一個字符串。因為 JS 最終都會被解釋成匯編的語言,匯編語言變量的類型肯定是要確定的,你把一個整型的改成了字符串,那解釋器就得做一些額外的處理。並且這種編碼風格是不提倡的,有一個變量第 1 行是一個整型,第 10 行變成了一個字符串,第 20 行又變成了一個 object,這樣就讓閱讀代碼的人比較困惑,上面明明是一個整數,怎么突然又變成一個字符串了。好的寫法應該是再定義一個字符串的變量:
(3)函數的返回類型應該是要確定的,例如下面不確定的寫法:
getPrice 這個函數有可能返回一個整數,也有可能返回一個空的字符串。這樣寫也不太好,雖然它是符合 JS 語法的,但這種編碼風格是不好的。使用你這個函數的人會有點無所適從,不敢直接進行加減乘除,因為如果返回字符串進行運算的話值就是 NaN 了。函數的返回類型應該是要確定的,如下面是返回整型
上面用了一個 function 制造一個局部作用域,也可以用 ES6 的塊級作用域。由於 map 這個變量直接在當前的局部作用域命中了,所以就不用再往上一級的作用域(這里是全局作用域)查找了,而局部作用域的查找是很快的。同時直接在全局作用域定義變量,會污染 window 對象。
(2)不要濫用閉包
閉包的作用在於可以讓子級作用域使用它父級作用域的變量,同時這些變量在不同的閉包是不可見的。這樣就導致了在查找某個變量的時候,如果當前作用域找不到,就得往它的父級作用域查找,一級一級地往上直到找到了,或者到了全局作用域還沒找到。因此如果閉包嵌套得越深,那么變量查找的時間就越長。
上面的代碼定義了一個 process 函數,在這個函數里面 count 變量的查找時間要高於局部的 factor 變量。其實這里不太適合用閉包,可以直接把 count 傳給 process:
這樣 count 的查找時間就和 factor 一樣,都是在當前作用域直接命中。這個就啟示我們如果某個全局變量需要頻繁地被使用的時候,可以用一個局部變量緩存一下,如下:
上面的兩個例子都是確定類型的,一個是字符串,一個是整數。就沒必要使用==了,直接用===就可以了。
(2)如果類型不確定,那么應該手動做一下類型轉換,而不是讓別人或者以后的你去猜這里面有類型轉換,如下:
(3)使用==在 JSLint 檢查的時候是不通過的:
(4)並且使用==可能會出現一些奇怪的現象,這些奇怪的現象可能會給代碼埋入隱患:
上面的比較在用===的時候都是 false,這樣才是比較合理的。例如第一點 null 居然會等於 undefined,就特別地奇怪,因為 null 和 undefined 是兩個毫無關系的值,null 應該是作為初始化空值使用,而 undefined 是用於檢驗某個變量是否未定義。這和第 1 點介紹強類型的思想是相通的。
4. 合並表達式
如果用 1 句代碼就可以實現 5 句代碼的功能,那往往 1 句代碼的執行效率會比較高,並且可讀性可能會更好
(1)用三目運算符取代簡單的 if-else
如上面的 getPrice 函數:
這個比寫一個 if-else 看起來清爽多了。當然,如果你寫了 if-else,壓縮工具也會幫你把它改三目運算符的形式:
(2)連等
連等是利用賦值運算表達式會返回所賦的值,並且執行順序是從右到左的,如下:
有時候你會看到有人這樣寫:
也是利用了賦值表達式會返回一個值,在 if 里面賦值的同時用它的返回值做判斷,然后 else 里面就已經有值了。上面的+號把字符串轉成了整數。
(3)自增
利用自增也可以簡化代碼。如下,每發出一條消息,localMsgId 就自增 1:
5. 減少魔數
例如,在某個文件的第 800 行,冒出來了一句:
dialogHandler.showQuestionNaire("seller", "sell", 5, true);
就會讓人很困惑了,上面的四個常量分別代表什么呢,如果我不去查一個那個函數的變量說明就不能夠很快地意會到這些常量分別有什么用。這些意義不明的常量就叫“魔數”。所以最好還是給這些常量取一個名字,特別是在一些比較關鍵的地方。例如上面的代碼可改成:
這樣意義就很明顯了。
6. 使用 ES6 簡化代碼
ES6 已經發展很多年了,兼容性也已經很好了。恰當地使用,可以讓代碼更加地簡潔優雅。
(1)使用箭頭函數取代小函數
有很多使用小函數的場景,如果寫個 function,代碼起碼得寫 3 行,但是用箭頭函數一行就搞定了,例如實現數組從大到小排序:
var nums = [4, 8, 1, 9, 0];``nums.sort(a, b => b - a);
代碼看起來簡潔多了,還有 setTimeout 里面經常會遇到只要執行一行代碼就好了,寫個 function 總感覺有點麻煩,用字符串的方式又不太好,所以這種情況用箭頭函數也很方便:
setTimeout(() => console.log("hi"), 3000)
箭頭函數在 C++/Java 等其它語言里面叫做 Lambda 表達式,Ruby 比較早就有這種語法形式了,后來 C++/Java 也實現了這種語法。當然箭頭函數或者 Lambda 表達式不僅適用於這種一行的,多行代碼也可以,不過在一行的時候它的優點才比較明顯。
(2)使用 ES6 的 class
雖然 ES6 的 class 和使用 function 的 prototype 本質上是一樣的,都是用的原型。但是用 class 可以減少代碼量,同時讓代碼看起來更加地高大上,使用 function 要寫這么多.