原文做者:Sébastien Castieljavascript
原文連接:Writing modern JavaScript codehtml
說點什麼:這是一篇很樸素的文章,講的道理都懂,但實際上,在工做中遇到相似的情形卻未必如此,編寫可維護,可閱讀,更安全的代碼是咱們應有的責任。前端
是否是還認爲 JavaScript 是一門用於在光標懸浮時改變頁面元素的語言?這些日子已經不復存在,每一種語言都在隨着時間推移而發展,咱們使用語言的方式一樣也在發展。看一下你一兩年前寫的代碼:會感到羞愧嗎?若是是的話,這篇文章應該很適合你。java
這裏會列出一些所謂的最佳實踐,目的是讓你的 JavaScript 代碼更容易編寫,閱讀和維護。react
第一個建議是使用 linter 工具,能夠幫助你檢查在不一樣文件是否遵照一致的規則,尤爲是當不一樣開發人員在同一個項目上工做:縮進,括號中的空格,替換 ==
爲 ===
...webpack
但更重要的是,儘量使用 linter 工具自動修復代碼。ESLint 就作得很好(帶有 --fix
選項),並且與全部主流 IDE 完美集成,能夠在保存時自動修復文件。git
還可使用 Prettier,不過這款工具更注重格式化而不是靜態檢查,但處理後的結果基本相同。es6
下一步將介紹與 linter 工具一塊兒使用的規則:github
若是不知道你的代碼須要什麼樣的規則,能夠參考:StandardJS。這是一個很是嚴格的 linter,沒法修改配置,但裏面的每一條規則已經愈來愈多地被社區接納。好比:web
使用 2 個空格縮進(我曾經使用 4 個空格,但實際使用起來 2 個空格很不錯)
不使用分號(一開始可能會以爲奇怪,但幾天後就再也回不去了)
在關鍵字(如 if)和花括號使用空格,在括號不使用空格
等等。
StandardJS 是一個獨立的 Node 模塊,能夠進行 lint 和修復代碼,但若是要在現有的大型項目中使用,而且想要停用一些規則(由於有些地方可能須要做大量修改),還可使用 ESLint 預約配置。好比,我就停用了規則 no-mixed-operators 和 import / no-webpack-loader-syntax。
若是你在使用 JavaScript 開發,根本沒辦法不據說 ES2015 +(或 ES6,ES7 ...)的特性。有的已是我離不開的:
箭頭函數:對於函數式編程,好比寫 x => x * 2
這樣的函數很是有用(見下一點)
類:中止使用原型函數,使用類更酷炫(但不要濫用,JavaScript 比任何面向對象的語言好多了)
對數組和對象的操做:
function doSomething() { const a = doSomethingElse() const b = doSomethingWithA(a) const otherResults = { c: '?', d: '?' } return { a, b, ...otherResults } // equivalent to { a: a, b: b } } const { a, c, ...rest } = doSomething() // Also works with arrays! // `rest` looks like { b: ..., d: '?' }
使用 async/await
編寫更簡單的異步處理:
// Please try to write the same code with classic promises ;) async function doSomething() { const a = await getValueForA() const b = await getValueForBFromA(a) const [c, d] = await Promise.all([ // parallel execution getValueForC(), getValueForDFromB(b) ]) const total = await calculateTotal(a, b, c, d) return total / 1000 }
想知道如何使用這些特性呢?個人另外一篇文章能給一些建議。(順便說一下,使用最新版本的 Node.js,可能再也不須要 Babel 就能使用這些新特性)
函數式編程最近很熱門,取得很多成就,並且不只僅是在 JavaScript 中。爲何呢?函數式編程能使代碼更具可預測性,肯定性,更安全,一旦習慣這種方式,代碼會更容易維護。這裏有一些簡單的建議:
首先,中止使用 for 循環,在大多數(多是全部?)狀況下根本不須要。例如:
const arr = [{ name: 'first', value: 13 }, { name: 'second', value: 7 }] // Instead of: const res = {} for (let i = 0; i < arr.length; i++) { const calculatedValue = arr[i].value * 10 if (calculatedValue > 100) { res[arr[i].name] = calculatedValue } } // Prefer: const res = arr .map(elem => ({ name: elem.name, calculatedValue: elem.value * 10 })) .filter(elem => elem.calculatedValue > 100) .reduce((acc, elem) => ({ [elem.name]: elem.calculatedValue, ...acc }), {})
好吧,這其實是一個很是極端的例子,對於不習慣函數式編程的人而言,可能看起來更加複雜。但咱們能夠稍微簡化一下:
const enrichElementWithCalculatedValue = elem => ({ name: elem.name, calculatedValue: elem.value * 10 }) const filterElementsByValue = value => elem => elem.calculatedValue > value const aggregateElementInObject = (acc, elem) => ({ [elem.name]: elem.calculatedValue, ...acc }) const res = arr .map(enrichElementWithCalculatedValue) .filter(filterElementsByValue(100)) .reduce(aggregateElementInObject, {})
在這裏,咱們定義了三個函數,其功能基本上與其名字一致。第二個建議:建立局部函數(即便是在已經存在的函數中)來講明代碼的功能,不須要使用註釋。
注意,三個局部函數不修改它們的執行上下文。沒有外部變量被修改,沒有其餘服務被調用...在函數式編程中,它們被稱爲純函數。純函數具備很大的優點:
很容易測試,由於從給定參數只有一個可能的結果,無論被調用了多少次;
不管應用狀態如何,都能保證相同的結果;
應用狀態在函數調用以前和以後保持不變。
因此個人第三個建議是:儘量地使用純函數。
習慣於使用異步代碼,並多使用 promise,看看 RxJS 的 observales(有一個很棒的教程關於從函數式編程到響應式編程)
寫測試!這應該是很明顯的,可是據我所知不少項目都有未經測試的代碼,儘管測試 JavaScript(前端或後端)並不困難。
使用最新的語言特性:好比不要再寫 arr.indexOf(elem) !== -1
,而應該寫成 arr.includes(elem)
。
大量閱讀技術文章:JavaScript subreddit 是瞭解目前社區最酷作法的一個很好的來源。
總而言之,最好的建議就是:老是重構你的代碼。好比改進你一年前寫過的模塊?藉此機會,用 const
取代 var
,使用箭頭函數或 async/await
簡化代碼......和你喜歡的代碼工做一件很愉悅的事。