1. 程式人生 > 實用技巧 >14個 JavaScript 程式碼優化技巧

14個 JavaScript 程式碼優化技巧

1、刪除未使用的程式碼和功能你的應用程式包含的程式碼越多,就需要將更多的資料傳輸到客戶端。瀏覽器也需要更多時間來分析和解釋程式碼。有時,你可能打包了很多根本用不到的功能。最好只在開發環境中保留這些額外的程式碼,而不要將其推送到生產環境中,以免給客戶端的瀏覽器增加負擔。要不斷問自己,某個功能或程式碼段是否是必要的。你可以手動移除未使用的程式碼,也可以使用 Uglify 或谷歌的 Closure Compiler 之類的工具刪除它們。你還可以使用一種被稱為搖樹優化的技術從應用程式中刪除未使用的程式碼。Webpack 這類打包軟體提供了這種技術,詳情可以參考這裡:https://www.infoq.cn/article/dcKcJiT8aeEBNZbdotFF如果要刪除未使用的 npm 軟體包,可以使用命令 npm prune,詳細資訊參考 NPM 文件。https://docs.npmjs.com/cli-commands/prune.html

2、儘可能的快取快取可以減少延遲和網路流量,從而減少了顯示資源表示所需的時間,以提高網站的速度和效能。快取可以藉助 Cache API 或 HTTP caching 來實現。你可能想知道內容更改時會發生什麼。當滿足某些條件(例如釋出新內容)時,上述快取機制能夠處理和重新生成快取。

3、避免記憶體洩漏作為一種高階語言,JS 會負責一些底層管理工作,例如記憶體管理。垃圾回收是大多數程式語言共有的過程。用外行術語來說,垃圾收集就是收集並釋放已分配給物件,但目前尚未在程式的任何部分中使用的記憶體。在 C 這樣的程式語言中,開發人員必須使用 malloc() 和 dealloc() 函式來處理記憶體分配和釋放操作。雖然在 JavaScript 中垃圾回收是自動執行的,但在某些情況下它也不是完美的。在 JavaScript ES6 中,引入了 Map 和 Set 及其“weaker”的同級物件。被稱為 WeakMap 和 WeakSet 的“較弱”對應項持有對物件的“弱”引用。它們使未引用的值能夠被垃圾回收,從而防止記憶體洩漏。你可以在此處瞭解有關 WeakMaps 的更多資訊:https://blog.bitsrc.io/understanding-weakmaps-in-javascript-6e323d9eec81

4、儘早打破迴圈超大迴圈肯定會耗費很多的時間,所以你應該儘早打破這些超大迴圈。你可以用 break 關鍵字和 continue 關鍵字來做這件事,從而編寫更高效的程式碼。在下面的示例中,如果你沒有從迴圈中 break,則你的程式碼將迴圈執行 1000000000 (10億)次,顯然會過載的。

let arr = new Array(1000000000).fill('----');arr[970] = 'found';for (let i = 0; i < arr.length; i++) {  if (arr[i] === 'found') {        console.log("Found");        break
; }}

在下面的示例中,如果你在迴圈不符合你的條件時沒有 continue,則你仍將執行該函式 1000000000 次。我們僅在陣列元素處於偶數位置時處理它。這將迴圈執行減少了近一半。

let arr = new Array(1000000000).fill('----');arr[970] = 'found';for (let i = 0; i < arr.length; i++) {  if(i%2!=0){        continue;    };    process(arr[i]);}

你可以在此處詳細瞭解迴圈和效能的關係:https://www.oreilly.com/library/view/high-performance-javascript/9781449382308/ch04.html

5、最小化變數計算的次數為了減少計算變數的次數,可以使用閉包。通俗來說,JavaScript 中的閉包使你可以從內部函式訪問外部函式作用域。每次建立函式(不呼叫)時都會建立閉包。內部函式將有權訪問外部作用域的變數,即使在返回外部函式之後也是如此。我們來看兩個例子。這些示例均來自 Bret 的部落格。

function findCustomerCity(name) {  const texasCustomers = ['John', 'Ludwig', 'Kate'];  const californiaCustomers = ['Wade', 'Lucie','Kylie'];
  return texasCustomers.includes(name) ? 'Texas' :    californiaCustomers.includes(name) ? 'California' : 'Unknown';};

如果你多次呼叫上面的函式,那麼每次都會建立一個新物件。每次呼叫時,變數 texasCustomers 和 californiaCustomers 都會導致不必要的記憶體重分配。

function findCustomerCity() {  const texasCustomers = ['John', 'Ludwig', 'Kate'];  const californiaCustomers = ['Wade', 'Lucie','Kylie'];
  return name => texasCustomers.includes(name) ? 'Texas' :    californiaCustomers.includes(name) ? 'California' : 'Unknown';};let cityOfCustomer = findCustomerCity();cityOfCustomer('John');//TexascityOfCustomer('Wade');//CaliforniacityOfCustomer('Max');//Unknown

在上面的示例中,藉助於閉包,返回到變數 cityOfCustomer 的內部函式可以訪問外部函式 findCustomerCity() 的常量。而且,每當以傳遞的名稱作為引數呼叫內部函式時,都無需再次例項化常量。要了解關於閉包的更多資訊,建議你閱讀 Prashant 的部落格文章:https://medium.com/@prashantramnyc/javascript-closures-simplified-d0d23fa06ba4

6、儘量減少 DOM 訪問與其他 JavaScript 語句相比,訪問 DOM 的速度很慢。如果你對 DOM 進行更改,觸發了佈局的重新繪製,那麼就得等好一陣子了。為了減少訪問 DOM 元素的次數,請先訪問一次,然後將其用作區域性變數。完成需求後,請一定將其設定為 null 來移除該變數的值。這將防止記憶體洩漏,因為這會觸發垃圾回收過程。

7、壓縮檔案通過壓縮方法(例如 Gzip)可以減小 JavaScript 檔案的大小。較小的檔案會提升你的網站效能,因為瀏覽器只需下載較小的資產即可。這類壓縮手段最多可以減少 80%的檔案大小。在此處閱讀有關壓縮的更多資訊:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#text_compression_with_gzip

8、縮小最終程式碼有人認為縮小和壓縮是相同的,其實不然。在壓縮中,我們使用特殊演算法來改變檔案的輸出大小;在縮小時,我們需要刪除 JavaScript 檔案中的註釋和多餘的空格。可以在網上找到許多工具和軟體包來幫助完成這一過程。縮小已成為頁面優化的標準做法,也是前端優化的主要步驟之一。縮小可以讓檔案大小最多減少 60%。你可以在此處閱讀有關縮小的更多資訊:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#minification_preprocessing_context-specific_optimizations

9、使用 Throttle 和 Debounce我們可以使用 Throttle(節流)和 Debounce(防抖)這兩種技術來嚴格控制程式碼需要處理事件的次數。節流是指定函式可以超時的最大次數。例如,“每 1000 毫秒最多執行一次 onkeyup 事件函式”。也就是說哪怕你每秒敲 20 個鍵,該事件每秒也只會觸發一次。這將減少程式碼的負擔。另一方面,防抖是指定自上次執行相同函式以來再次執行該函式的最短持續時間。換句話說,“上次呼叫函式後過最少 600 毫秒才執行此函式”。要了解有關節流和防抖的更多資訊,這裡有一篇快速入門:https://css-tricks.com/the-difference-between-throttling-and-debouncing/你可以實現自己的防抖和節流函式,也可以從 Lodash 和 Underscore 之類的庫中匯入它們。

10、避免使用 Delete 關鍵字delete 關鍵字用於從物件中刪除屬性。這個關鍵字的效能表現不怎麼好,預計它將在未來的更新中修復。或者,你可以簡單地將不需要的屬性設定為 undefined。

const object = {name:"Jane Doe", age:43};object.age = undefined;

你還可以使用 Map 物件,Bret 認為它的 delete 方法會更快。

11、使用非同步程式碼防止執行緒阻塞你應該知道 JavaScript 預設情況下是同步的和單執行緒的。但是在某些情況下,你的程式碼需要很大的計算量。程式碼本質上是同步的,意味著一段程式碼執行時將阻止其他程式碼語句執行,直到前者完成執行為止。這會降低整體效能。但是我們可以通過非同步程式碼來避免這種情況。非同步程式碼以前以回撥的形式編寫,但是 ES6 引入了一種處理非同步程式碼的新樣式。這種新樣式被稱為 Promise。你可以在 MDN 的官方文件中瞭解有關回調和 Promise 的更多資訊。https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing可是等等……JavaScript 預設情況下是同步的,並且也是單執行緒的。如何在單個執行緒上執行非同步程式碼呢?這是很多人感到困惑的地方。做到這一點,主要依賴執行在瀏覽器後臺的 JavaScript 引擎。JavaScript 引擎是執行 JavaScript 程式碼的計算機程式或直譯器。JavaScript 引擎可以用多種語言編寫。例如,支援 Chrome 瀏覽器的 V8 引擎是用 C++ 編寫的,而支援 Firefox 瀏覽器的 SpiderMonkey 引擎是用 C 和 C++ 編寫的。這些 JavaScript 引擎可以在後臺處理任務。根據 Brian 的說法,呼叫棧可以識別 Web API 的函式,並將其交給瀏覽器處理。瀏覽器完成這些任務後,它們將返回並作為回撥被推上堆疊。你可能想知道 Node.js 是怎麼做這些工作的,畢竟它沒有瀏覽器的幫助。實際上,支援 Chrome 的那個 V8 引擎也是 Node.js 背後的支撐。這裡有 Salil 的一篇很棒的部落格文章,解釋了 Node 生態系統中的這一過程。https://medium.com/better-programming/is-node-js-really-single-threaded-7ea59bcc8d64

12、使用程式碼拆分如果你有使用 Google Light House 的經驗,肯定會熟悉一種稱為“first contentful paint”的指標。它是 Lighthouse 報告的 Performance 部分中跟蹤的六個指標之一。First Contentful Paint(FCP)衡量使用者轉到你的頁面後瀏覽器渲染第一段 DOM 內容所花費的時間。頁面上的影象、非白色<canvas>元素和 SVG 被視為 DOM 內容;iframe 內部不包含任何內容。獲得更高的 FCP 分數的最佳方法之一是使用程式碼拆分。程式碼拆分是一種在傳輸開始時僅將必要的模組傳送給使用者的技術。通過減小最初發送的載荷大小,這將極大地影響 FCP 分數。流行的模組打包器(例如 webpack)可為你提供程式碼拆分功能。你還可以利用原生 ES 模組來單獨載入各個模組。你可以在此處詳細瞭解有關原生 ES 模組的資訊。https://blog.bitsrc.io/understanding-es-modules-in-javascript-a28fec420f73

13、使用 async 和 defer在現代網站中,指令碼比 HTML 更為密集,其大小更大且消耗更多的處理時間。預設情況下,瀏覽器必須等待指令碼下載和執行完畢後,再處理頁面的其餘部分。於是笨重的指令碼可能會阻止網頁的載入。為了避免這種情況,JavaScript 為我們提供了兩種分別稱為 async 和 defer 的技術。你只需將這些屬性新增到<script>標記中即可。Async 會讓瀏覽器在不影響渲染的情況下載入指令碼。換句話說,頁面不會等待 async 指令碼,而是先處理和顯示內容。Defer 是讓瀏覽器在渲染完成後載入指令碼。如果同時指定它們兩者,則 async 在現代瀏覽器上更優先,而支援 defer 但不支援 async 的老式瀏覽器將回退為 defer。這兩個屬性可以幫助你大幅減少頁面載入時間。我強烈建議你閱讀 Flavio 的這篇部落格文章。https://flaviocopes.com/javascript-async-defer/

14、在後臺執行 CPU 密集型任務可以使用 Web Worker 在後臺執行緒中執行指令碼。如果你有一些高強度的任務,可以將它們分配給 Web Worker,這些 WebWorker 可以在不干擾使用者介面的情況下執行它們。建立後,Web Worker 可以將訊息釋出到該程式碼指定的事件處理程式來與 JavaScript 程式碼通訊,反之亦然。