作為開發人員,我們一直在尋找讓我們的程式碼更快更好的方法。但在此之前,編寫高效能程式碼需要做三件事:
瞭解語言及其工作原理基於用例進行設計除錯!修復!重複記住這一點:任何傻瓜都可以編寫計算機可以理解的程式碼,優秀的程式設計師編寫人類可以
理解的程式碼。
我們來看看如何使 JavaScript程式碼執行得更快。
延遲延遲演算法將計算延遲到需要執行時才執行,然後生成結果。
const someFn = () => { doSomeOperation() return () => { doExpensiveOperation() }}const t = someArray.filter((x) => checkSomeCondition(x)).map((x) => someFn(x))// 現在,如果有需要在執行t.map((x) => t())1234567891011
最快的程式碼是未執行的程式碼,所以儘量延遲執行。
JavaScript 使用原型繼承,JS 中所有物件都是Object的例項。
MDN說:嘗試訪問物件的屬性時,不僅會在物件上搜索該屬性,還會在物件的原型,原型的原型等上搜索該屬性,直到找到匹配屬性名或原型鏈的末端。
對於每個屬性,JavaScript引擎都必須遍歷整個物件鏈,直到找到匹配項。 如果使用不當,這會佔用大量資源,並影響應用程式的效能。
所以不要這樣:
const name = userResponse.data.user.firstname + userResponse.data.user.lastname1
而是這樣做:
const user = userResponse.data.userconst name = user.firstname + user.lastname12
使用臨時變數來儲存連結的屬性,而不是遍歷訪問整條鏈。
使用轉譯器之前要三思在上述情況下,userResponse可能不是物件,如果是物件,它的屬性 user 也可能不是物件。所以,在獲取值時要進行檢查:
let name = ''if (userResponse) { const data = userResponse.data if (data && data.user) { const user = data.user if (user.firstname) { name += user.firstname } if (user.lastname) { name += user.firstname } }}12345678910111213
這太囉嗦了。程式碼越多,bug 就越明顯。我們能把它縮小嗎?當然,可以使用 JS 中可選的連結、解構賦值來最佳化它。
const user = userResponse?.data?.userconst { firstname = '', lastname = ''} = userconst name = firstname + lastname123
是不是很靈活地,簡短? 不要使用這個要注意,Babel 會按照以下方式進行轉換:
'use strict'var _userResponse, _userResponse$datavar user = (_userResponse = userResponse) === null || _userResponse === void 0 ? void 0 : (_userResponse$data = _userResponse.data) === null || _userResponse$data === void 0 ? void 0 : _userResponse$data.uservar _user$firstname = user.firstname, firstname = _user$firstname === void 0 ? '' : _user$firstname, _user$lastname = user.lastname, lastname = _user$lastname === void 0 ? '' : _user$lastnamevar name = firstname + lastname12345678910111213141516
當使用轉譯時,確保你選擇了一個更適合你的用例的。
瞭解SMI和堆號數字很奇怪,ECMAScript將數字標準化為64位浮點值,也稱為雙精度浮點或Float64表示形式。
如果 JS 引擎以Float64表示形式儲存數字,則將導致巨大的效能低下。 JS 引擎對數字進行抽象,使其行為與Float64完全匹配。 與float64運算相比,JS 引擎執行整數運算的速度要快得多。
有時,我們認為像下面這樣寫法可讀比較好:
const maxWidth = '1000'const minWidth = '100'const margin = '10'getWidth = () => ({ maxWidth: maxWidth - margin * 2, minWidth: minWidth - margin * 2,})1234567
評估區域性變數
如果getWidth函式被多次呼叫,那麼每次呼叫它時都會計算它的值。上面的計算並不是什麼大問題,因此我們不會注意到任何效能影響。
但是總的來說,執行時的求值的數量越少,效能就越好。
// maxWidth - (margin * 2)const maxWidth = '980'// minWidth - (margin * 2)const minWidth = '80'const margin = '10'getWidth = () => ({ maxWidth, minWidth,})123456789
使用 Map 而不是 switch/if-else 條件如果要檢查多個條件時,可以使用Map代替 switch/if-else條件。 在Map中查詢元素的效能比對switch和if-else條件快得多。
switch (day) { case 'monday': return 'workday' case 'tuesday': return 'workday' case 'wednesday': return 'workday' case 'thursday': return 'workday' case 'friday': return 'workday' case 'saturday': return 'funday' case 'sunday': return 'funday'}// or thisif ( day === 'monday' || day === 'tuesday' || day === 'wednesday' || day === 'thursday' || day === 'friday') return 'workday'else return 'funday'12345678910111213141516171819202122232425262728
上面可以使用 Map 來代替
const m = new Map([ ['monday','workday'], ['tuesday', 'workday'], ['wednesday', 'workday'], ['thursday', 'workday'], ['friday', 'workday'], ['saturday', 'funday'], ['sunday', 'funday']];return m.get(day);1234567891011
if-else 排序
在 React元件中,這種寫法還是很常見的。
export default function UserList(props) { const { users } = props if (users.length) { return <UserList /> } return <EmptyUserList />}123456789
在這裡,我們在沒有使用者時渲染<EmptyUserList />否則渲染<UserList />。 有大部分人認為,我們首先處理所有空的的情況,然後再處理有資料的情況。 對於任何讀過它的人來說都更清楚,而且效率更高。 也就是說,以下程式碼比上一個程式碼更有效。
export default function UserList(props) { const { users } = props if (!users.length) { return <EmptyUserList /> } // some resource intensive operation return <UserList />}12345678910
當然 users.length 一直有值的話,就使用第一種情況。
型別是你最好的朋友JavaScript是解釋型和編譯型語言。 為了產生更有效的二進位制檔案,編譯器需要型別資訊。 但是,作為一種動態型別化的語言會使編譯器難以進行。
編譯器在編譯熱程式碼(多次執行的程式碼)時進行一些假設並最佳化程式碼。 編譯器花費一些時間來生成此最佳化的程式碼。 當這些假設失敗時,編譯器必須丟棄最佳化的程式碼,並退回到解釋的執行方式。 這是耗時且昂貴的。