JavaScript優(yōu)化技巧
作為開發(fā)人員,我們一直在尋找讓我們的代碼更快更好的方法。但在此之前,編寫高性能代碼需要做三件事:
- 了解語言及其工作原理
- 基于用例進(jìn)行設(shè)計
- 調(diào)試!修復(fù)!重復(fù)
記住這一點
任何傻瓜都可以編寫計算機(jī)可以理解的代碼,優(yōu)秀的程序員編寫人類可以理解的代碼。- 丁·福勒
我們來看看如何使 JavaScript代碼運行得更快。
延遲
延遲算法將計算延遲到需要執(zhí)行時才執(zhí)行,然后生成結(jié)果。
- const someFn = () => {
- doSomeOperation()
- return () => {
- doExpensiveOperation()
- }
- }
- const t = someArray.filter((x) => checkSomeCondition(x)).map((x) => someFn(x))
- // 現(xiàn)在,如果有需要在執(zhí)行
- t.map((x) => t())
最快的代碼是未執(zhí)行的代碼,所以盡量延遲執(zhí)行。
JavaScript 使用原型繼承,JS 中所有對象都是Object的實例。
MDN說:
嘗試訪問對象的屬性時,不僅會在對象上搜索該屬性,還會在對象的原型,原型的原型等上搜索該屬性,直到找到匹配屬性名或原型鏈的末端。
對于每個屬性,JavaScript引擎都必須遍歷整個對象鏈,直到找到匹配項。如果使用不當(dāng),這會占用大量資源,并影響應(yīng)用程序的性能。
所以不要這樣:
- const name = userResponse.data.user.firstname + userResponse.data.user.lastname
而是這樣做:
- const user = userResponse.data.user
- const name = user.firstname + user.lastname
使用臨時變量來保存鏈接的屬性,而不是遍歷訪問整條鏈。
使用轉(zhuǎn)譯器之前要三思
在上述情況下,userResponse可能不是對象,如果是對象,它的屬性 user 也可能不是對象。所以,在獲取值時要進(jìn)行檢查:
- 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
- }
- }
- }
這太啰嗦了。代碼越多,bug 就越明顯。我們能把它縮小嗎?當(dāng)然,可以使用 JS 中可選的鏈接、解構(gòu)賦值來優(yōu)化它。
- const user = userResponse?.data?.user
- const { firstname = '', lastname = ''} = user
- const name = firstname + lastname
是不是很靈活地,簡短?不要使用這個要注意,Babel 會按照以下方式進(jìn)行轉(zhuǎn)換:
- 'use strict'
- var _userResponse, _userResponse$data
- var user =
- (_userResponse = userResponse) === null || _userResponse === void 0
- ? void 0
- : (_userResponse$data = _userResponse.data) === null ||
- _userResponse$data === void 0
- ? void 0
- : _userResponse$data.user
- var _user$firstname = user.firstname,
- firstname = _user$firstname === void 0 ? '' : _user$firstname,
- _user$lastname = user.lastname,
- lastname = _user$lastname === void 0 ? '' : _user$lastname
- var name = firstname + lastname
當(dāng)使用轉(zhuǎn)譯時,確保你選擇了一個更適合你的用例的。
了解SMI和堆號
數(shù)字很奇怪,ECMAScript將數(shù)字標(biāo)準(zhǔn)化為64位浮點值,也稱為雙精度浮點或Float64表示形式。
如果 JS 引擎以Float64表示形式存儲數(shù)字,則將導(dǎo)致巨大的性能低下。JS 引擎對數(shù)字進(jìn)行抽象,使其行為與Float64完全匹配。與float64運算相比,JS 引擎執(zhí)行整數(shù)運算的速度要快得多。
有時,我們認(rèn)為像下面這樣寫法可讀比較好:
- const maxWidth = '1000'
- const minWidth = '100'
- const margin = '10'
- getWidth = () => ({
- maxWidth: maxWidth - margin * 2,
- minWidth: minWidth - margin * 2,
- })
評估局部變量
如果getWidth函數(shù)被多次調(diào)用,那么每次調(diào)用它時都會計算它的值。上面的計算并不是什么大問題,因此我們不會注意到任何性能影響。
但是總的來說,運行時的求值的數(shù)量越少,性能就越好。
- // maxWidth - (margin * 2)
- const maxWidth = '980'
- // minWidth - (margin * 2)
- const minWidth = '80'
- const margin = '10'
- getWidth = () => ({
- maxWidth,
- minWidth,
- })
使用 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 this
- if (
- day === 'monday' ||
- day === 'tuesday' ||
- day === 'wednesday' ||
- day === 'thursday' ||
- day === 'friday'
- )
- return 'workday'
- else return 'funday'
上面可以使用 Map 來代替
- const m = new Map([
- ['monday','workday'],
- ['tuesday', 'workday'],
- ['wednesday', 'workday'],
- ['thursday', 'workday'],
- ['friday', 'workday'],
- ['saturday', 'funday'],
- ['sunday', 'funday']
- ];
- return m.get(day);
if-else 排序
在 React組件中,這種寫法還是很常見的。
- export default function UserList(props) {
- const { users } = props
- if (users.length) {
- return <UserList />
- }
- return <EmptyUserList />
- }
在這里,我們在沒有用戶時渲染
- export default function UserList(props) {
- const { users } = props
- if (!users.length) {
- return <EmptyUserList />
- }
- // some resource intensive operation
- return <UserList />
- }
當(dāng)然 users.length 一直有值的話,就使用第一種情況。
類型是你最好的朋友
JavaScript是解釋型和編譯型語言。為了產(chǎn)生更有效的二進(jìn)制文件,編譯器需要類型信息。但是,作為一種動態(tài)類型化的語言會使編譯器難以進(jìn)行。
編譯器在編譯熱代碼(多次執(zhí)行的代碼)時進(jìn)行一些假設(shè)并優(yōu)化代碼。編譯器花費一些時間來生成此優(yōu)化的代碼。當(dāng)這些假設(shè)失敗時,編譯器必須丟棄優(yōu)化的代碼,并退回到解釋的執(zhí)行方式。這是耗時且昂貴的。
作者:EthicalAds 譯者:前端小智 來源: sendilkumarn
原文:https://sendilkumarn.com/blog/javascript-optimization/
本文轉(zhuǎn)載自微信公眾號「 大遷世界 」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系大遷世界公眾號。