高性能JavaScript筆記之數據存取 (上)

四種基本的數據存儲位置

數據的存儲位置會很大程度上影響其讀取速度。JavaScript 有四種基本的數據存儲的位置:

  • 字面量。字面量只代表自身,不存儲在特定位置。JavaScript 中的字面量有:字符串、數字、布爾值、對象、數組、函數、正則表達式,以及特殊的 null 和 undefined 值。
  • 本地變量。開發人員使用關鍵字 var 定義的數據存儲單元。
  • 數組元素。存儲在 JavaScript 數組對象內部,以數字做爲索引。
  • 對象成員。存儲在 JavaScript· 對象內部,以字符串作爲索引。

存取消耗的時間比較:

字面量 ≈ 本地變量 < 數組元素 ≈ 對象成員

建議:如果在乎運行速度,那麼儘量使用字面量和局部變量,減少數組項和對象成員的使用。

管理作用域

作用域鏈和標識符解析

執行函數時會創建一個稱爲執行環境(execution context)的內部對象。一個執行環境定義了一個函數執行時的環境。函數每次執行時對應的執行環境都是獨一無二的,所以多次調用同一個函數就會導致創建多個執行環境。當函數執行完畢,執行環境就會被摧毀。

在函數執行過程中,每遇到一個變量,都會經歷一次標識符解析過程以及決定從哪裏獲取或存儲數據。該過程搜索執行環境的作用域鏈,查找同名的標識符。搜索過程從作用域鏈頭部開始,也就是當前運行函數的活動對象。如果沒找到,則繼續搜素作用域鏈中的下一個對象。正是這個搜索過程影響了性能。

標識符解析的性能

在執行環境中的作用域鏈中,一個標識符位置越深,它的讀寫速度就越慢。因此函數中讀寫局部變量總是最快的,而讀寫全局變量通常是最慢的,因爲全局變量總是存在於執行環境作用域鏈的最末端。

建議:儘量使用局部變量。一個好的經驗法則是:如果某個跨作用域的值在函數中被引用一次以上,那麼就把它儲存到局部變量裏。

閉包性能問題

思考以下代碼,嘗試理解與閉包有關的性能問題:

function assignEvent() {
	var id = "xdi9592";

	document.getElementById("save-btn").onclick = function(event) {
		saveDocument(id);
	};
}

onclick 事件是一個閉包,它在 assignEvent() 執行時創建。
當 assignEvent() 函數執行時,一個包含變量 id以及其他數據的活動對象被創建。他成爲執行環境作用域鏈中的第一個對象,而全局變量緊隨其後。當閉包被創建時,它的[[scope]]屬性被初始化爲這些對象
在這裏插入圖片描述
由於閉包的[[scope]]屬性包含了與執行環境作用域鏈相同的對象的引用,因此會產生副作用。通常來說,函數的活動對象會隨着執行環境一同摧毀,但引入閉包後,由於引用閉包仍然存在閉包的[[scope]]屬性中,因此激活對象無法被摧毀。這意味着腳本中的閉包與非閉包函數相比,需要更多的內存開銷。

當閉包代碼被執行時,會創建一個執行環境,它的作用域鏈與屬性[[scope]]中所引用的兩個相同的作用域鏈對象一起被初始化,然後一個活動對象爲閉包自身對象所創建。
在這裏插入圖片描述
注意閉包中用到的兩個標識符,id 和 saveDocument,它們的位置在作用域鏈的第一個對象之後。這就是使用閉包最需要關注的性能點:在頻繁訪問跨作用域的標識符時,每次訪問都會帶來性能損失。

建議:將常用的跨作用域變量存儲在局部變量中,然後直接訪問局部變量。

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