14 個 JavaScript 代碼優化技巧

雲棲號資訊:【點擊查看更多行業資訊
在這裏您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

JavaScript 已經成爲有史以來最受歡迎的編程語言之一。根據 W3Tech 的數據,全世界將近 96%的網站都在使用它。關於 Web 有一個關鍵的事實是,你無法控制訪問網站的用戶所用設備的硬件規格。最終用戶訪問你的網站時,使用的可能是高端設備也可能是低端設備,網絡連接條件也有好有差。這意味着你必須儘可能優化自己的網站,以滿足任何用戶的需求。

這篇文章列舉了一些技巧,可幫助你寫出更好的 JavaScript 代碼,從而提高性能。

附帶提一下,請共享和重用你的 JS 組件,以在高質量代碼(寫起來需要花費時間)和合理的交付時間之間保持適當的平衡。你可以使用 Bit 等流行工具將任何項目中的組件(普通 JS、TS、React、Vue 等)共享到 Bit 的組件中心,用不了多大功夫。

1、刪除未使用的代碼和功能
你的應用程序包含的代碼越多,就需要將更多的數據傳輸到客戶端。瀏覽器也需要更多時間來分析和解釋代碼。
有時,你可能打包了很多根本用不到的功能。最好只在開發環境中保留這些額外的代碼,而不要將其推送到生產環境中,以免給客戶端的瀏覽器增加負擔。

要不斷問自己,某個功能或代碼段是否是必要的。
你可以手動移除未使用的代碼,也可以使用 Uglify 或谷歌的 Closure Compiler 之類的工具刪除它們。你甚至可以使用一種稱爲搖樹優化的技術從應用程序中刪除未使用的代碼。Webpack 這類打包軟件提供了這種技術,詳情可以參考這裏。如果要刪除未使用的npm 軟件包,可以使用命令npm prune,詳細信息參考 NPM 文檔。

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

3、避免內存泄漏
作爲一種高級語言,JS 會負責一些底層管理工作,例如內存管理。垃圾回收是大多數編程語言共有的過程。用外行術語來說,垃圾收集就是收集並釋放已分配給對象,但目前尚未在程序的任何部分中使用的內存。在 C 這樣的編程語言中,開發人員必須使用 malloc() 和 dealloc() 函數來處理內存分配和釋放操作。

雖然在 JavaScript 中垃圾回收是自動執行的,但在某些情況下它也不是完美的。在 JavaScript ES6 中,引入了 Map 和 Set 及其“weaker”的同級對象。被稱爲 WeakMap 和 WeakSet 的“較弱”對應項持有對對象的“弱”引用。它們使未引用的值能夠被垃圾回收,從而防止內存泄漏。你可以在此處閱讀有關 WeakMaps 的更多信息。

4、儘早打破循環
超大循環肯定會消耗很多寶貴的時間,所以你應該儘早打破它們。你可以用 break 關鍵字和 continue 關鍵字來做這件事。編寫最高效的代碼是你的責任。
在下面的示例中,如果你沒有從循環中 break,則你的代碼將循環運行 1000000000 次,顯然會過載的。

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]);
}

你可以在此處詳細瞭解循環和性能的關係。

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');//Texas
cityOfCustomer('Wade');//California
cityOfCustomer('Max');//Unknown

在上面的示例中,藉助於閉包,返回到變量 cityOfCustomer 的內部函數可以訪問外部函數 findCustomerCity() 的常量。而且,每當以傳遞的名稱作爲參數調用內部函數時,都無需再次實例化常量。要了解關於閉包的更多信息,建議你閱讀 Prashant 的博客文章。

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

7、壓縮文件
通過壓縮方法(例如 Gzip)可以減小 JavaScript 文件的大小。較小的文件會提升你的網站性能,因爲瀏覽器只需下載較小的資產即可。
這類壓縮手段最多可以減少 80%的文件大小。在此處閱讀有關壓縮的更多信息。

8、縮小最終代碼
有人認爲縮小和壓縮是相同的,其實不然。在壓縮中,我們使用特殊算法來改變文件的輸出大小;在縮小時,我們需要刪除 JavaScript 文件中的註釋和多餘的空格。可以在網上找到許多工具和軟件包來幫助完成這一過程。縮小已成爲頁面優化的標準做法,也是前端優化的主要步驟之一。
縮小可以讓文件大小最多減少 60%。你可以在此處閱讀有關縮小的更多信息。

9、使用 Throttle(節流)和 Debounce(防抖)
我們可以使用這兩種技術來嚴格控制代碼需要處理事件的次數。
節流是指定函數可以超時的最大次數。例如,“每 1000 毫秒最多執行一次 onkeyup 事件函數”。也就是說哪怕你每秒敲 20 個鍵,該事件每秒也只會觸發一次。這將減少代碼的負擔。
另一方面,防抖是指定自上次執行相同函數以來再次運行該函數的最短持續時間。換句話說,“上次調用函數後過最少 600 毫秒才執行此函數”。要了解有關節流和防抖的更多信息,這裏有一篇快速入門。
你可以實現自己的防抖和節流函數,也可以從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 的更多信息。
可是等等……
JavaScript 默認情況下是同步的,並且也是單線程的。
如何在單個線程上運行異步代碼呢?這是很多人感到困惑的地方。做到這一點,主要依賴運行在瀏覽器後臺的 JavaScript 引擎。JavaScript 引擎是執行 JavaScript 代碼的計算機程序或解釋器。JavaScript 引擎可以用多種語言編寫。例如,支持 Chrome 瀏覽器的 V8 引擎是用 C++ 編寫的,而支持 Firefox 瀏覽器的 SpiderMonkey 引擎是用 C 和 C++ 編寫的。
這些 JavaScript 引擎可以在後臺處理任務。根據 Brian 的說法,調用棧可以識別 Web API 的函數,並將其交給瀏覽器處理。瀏覽器完成這些任務後,它們將返回並作爲回調被推上堆棧。
你可能想知道 Node.js 是怎麼做這些工作的,畢竟它沒有瀏覽器的幫助。實際上,支持 Chrome 的那個 V8 引擎也是 Node.js 背後的支撐。這裏有 Salil 的一篇很棒的博客文章,解釋了Node 生態系統中的這一過程。

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

13、使用 async 和 defer
在現代網站中,腳本比 HTML 更爲密集,其大小更大且消耗更多的處理時間。默認情況下,瀏覽器必須等待腳本下載和執行完畢後,再處理頁面的其餘部分。
於是笨重的腳本可能會阻止網頁的加載。爲了避免這種情況,JavaScript 爲我們提供了兩種分別稱爲 async 和 defer 的技術。你只需將這些屬性添加到

14、使用 Web Workers 在後臺運行 CPU 密集型任務
Web Worker 允許你在後臺線程中運行腳本。如果你有一些高強度的任務,可以將它們分配給 Web Worker,這些 WebWorker 可以在不干擾用戶界面的情況下運行它們。創建後,Web Worker 可以將消息發佈到該代碼指定的事件處理程序來與 JavaScript 代碼通信,反之亦然。
要了解有關 Web Worker 的更多信息,建議你閱讀 MDN 文檔。

感謝閱讀,歡迎評論,編程愉快!

【雲棲號在線課堂】每天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/zhibo

立即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK

原文發佈時間:2020-07-21
本文作者:Mahdhi Rezvi
本文來自:“InfoQ”,瞭解相關信息可以關注“InfoQ

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章