寫出高性能javascript,必讀《一》

前言

這段時間把《高性能JavaScript》書籍讀完,受益良多。在讀書的過程把重點精簡地提煉並結合本身的經驗記錄下來。也但願看完這篇文章可以給對JavaScript多瞭解一點點。javascript

目錄

  • 加載和執行
  • 數據存取
  • DOM編程
  • 算法和流程控制
  • 快速響應的用戶界面
  • Ajax
  • 編程實戰
  • 構建並部署高性能的JavaScript應用

1. 加載和執行

瞭解: 當瀏覽器在執行JavaScript代碼時,是不可以同時作其餘事情。由於,絕大多數的瀏覽器使用單一線程來處理UI和JS。簡單地說,< script >標籤(內嵌或外鏈)的出現,頁面都會聽下來等待腳本下載並執行完成,由於腳本中可能會有修改頁面內容的操做。html

1.1 腳本的位置

<html>
	<head>
		<script 1></script>
		<script 2></script>
		<script 3></script>
	</head>
	<body>
		<div></div>
	</body>
</html>
複製代碼

這樣的代碼存在十分嚴重的性能問題,整個解析的過程會卡頓在<script 1,2,3>的下載和執行的過程。而頁面內容遲遲不能呈如今用戶面前。 記住: 瀏覽器在解析n以前不會渲染頁面的任何部分,此時的表現爲空白。因此 < script > 放在頂部的作法很是不可取。java

js文件的下載執行流程:: 正則表達式

在這裏插入圖片描述
==那麼該如何去改進?== !!!並行下載js (後來的IE八、FireFox3.五、Safari四、Chrome2都容許並行下載JS文件了,也就是說你如今接觸到的大多瀏覽器的JS文件的下載都是並行的) 也就是:::
在這裏插入圖片描述
JS文件是可以並行下載,可是頁面仍是會阻塞其餘資源下載(如圖片)而且會等待全部的JS文件下載完成並執行完成。 所以強烈推薦將全部的< script >標籤儘量的防盜標籤的底部,已儘可能減小對整個頁面的影響。

1.2 組織腳本

考慮到Http請求會有額外的開銷,因此script標籤的個數不能過多。這個時候就要考慮將js文件進行合併已減小數量了。不過如今的不少打包工具都有這樣的功能,因此不用太過關注這個問題。算法

1.3無阻塞腳本

減小JS文件的大小及數量是優化的第一步(畢竟效果有限,由於js總會愈來愈多,愈來愈大)因此要考慮無阻塞的加載腳本方式。 而==無阻塞腳本的祕訣在於:頁面加載完成後再就在JS代碼(即在window.load()方法出發後再下載)==chrome

1.4延遲的腳本

Defer屬性: HTML4爲< script >標籤訂義了defer擴展屬性,該屬性指明的JS文件不會修改DOM。但一開始只有IE四、FireFox3.5+支持;不事後來已經被全部的主流瀏覽器所支持。後來的H5中還添加了async擴展屬性。 區別編程

defer async
並行下載JS 並行下載JS
等待頁面完成後執行(可是在load()方法調用以前) 下載完成後執行

1.5動態腳本

< script >標籤跟其餘的元素同樣,都能經過DOM操做。跨域

let scriptaEle = document.createElement('script');
scriptEle.type = 'text/script';
scriptEle.src = 'file1.js';
document.getElementByTagName('head')[0].appendChild(scriptEle);
複製代碼

==這個技術的重點在於:不管什麼時候啓動下載,文件的下載和執行的過程不會阻塞頁面的其餘過程。==數組

舒適提示:瀏覽器

  • 新建立的JS添加到< head >中會更好,由於在< body >中IE中可能會拋出「操做已終止」的信息
  • 下載的JS一般會當即執行,除了opera和firefox會等待此前全部的動態腳本節點執行完畢)

有時該JS會被其餘的JS調用其中的方法,因此有時須要監聽JS下載的狀態。

  • firefox、opera、chrome、safari3以上 | 完成時會觸發load()函數
  • IE爲< script >提供readystate屬性,但並非其中的全部狀態都會執行(一般loaded/complete出現一種就能夠)
全部狀態 Value
uninitialized 初始
loading 開始下載
loaded 下載完成
interactive 完成但未可用
complete 已準備就緒

==動態腳本憑藉其在跨瀏覽其兼容性和易用的優點,成爲最通用的無阻加載解決方案== 記住哈,想要優化JS的下載就採用動態腳本技術!!!

1.6 XMLHttpRequest腳本注入

**瞭解:**另外一種無阻塞加載腳本方式:使用XMLHttpRequest技術獲取腳本並注入頁面(也就是用XHR網絡請求JS而後注入到頁面中) 例如:

let xhr = new XMLHttpRequest();
xhr.open('get','file.js',true);
xhr.onReadyStateChange = function () {
	if(xhr.readyState === 4) {\
		if(xhr.state >= 200 && xhr.status < 300 || ==304) {
			//請求JS成功後,建立script標籤,將JS內容賦給script標籤;而後嵌入頁面 
			let script = document.createElement('script');
			script.type = 'text/javascript';
			script.text = xhr.responseText;
			document.body.appendChild(script);
		}
	}
}
// 發起請求
xhr.send();
複製代碼

優勢:

  • JS不會當即執行,能夠放到你準備好的時候再執行均可以(靈活控制JS的執行時間)
  • 全部主瀏覽器都支持

1.7 推薦的無阻塞模式

向頁面中添加大量JS的推薦作法: 第一步:先添加動態加載全部的代碼 第二部:再加載剩餘JS代碼

有一些如LazyLoad的類庫能夠協助咱們快速的使用無阻塞加載JS

2. 數據存取

瞭解: 計算機科學中有一個經典的問題:經過改變數據的存取位置來得到最佳的讀寫性能。數據的存取位置關係到代碼執行過程當中的檢索速度。

js中有四種基本的數據存取位置: 字面量 : 字面量只表明自己,不存儲在特定位置。 : 有:字符串、數字、布爾、數組、函數、正則表達式、nullull、undefined 本地變量 : 開發人員使用var、let等定義的數據存儲單元 數組元素 : 存儲在JS數組對象內部,以數字爲索引 對象成員 : 存儲在JS對象內部,以字符串爲索引

2.1 管理做用域

**瞭解:**做用域是理解JS的關鍵,因此要重點的搞明白這一部分,從性能和功能的角度去思考。

  • 肯定哪些變量能被訪問
  • 肯定this
  • 關係到性能

須要瞭解以上問題的原理!

2.2 做用域鏈和標誌符解析

**瞭解:**JS函數是Function對象,和普通對象同樣擁有

  • 可編程訪問的屬性

  • 不可經過代碼訪問的內部屬性(而這其中有着很是重要的 ==[scope]== )

    在這裏插入圖片描述
    函數執行時建立一個爲執行環境的內部對象。函數每次執行的都會建立獨一無二的執行環境。當函數執行完畢,執行環境就會被銷燬。 每一個執行環境都有本身的做用域鏈,用於解析標誌符,在函數執行的過程當中,每遇到一個變量,都會經歷標誌符解析的過程以決定從哪裏獲取存儲數據。從頂層的「活動對象」做用域開始便利做用域鏈,知道找到爲止。 ==正式這個搜索過程,影響性能==

    2.2 標誌符解析的性能

    一個標誌符所在的位置越深,他的讀寫速度越慢;因此全局變量訪問速度越慢(由於他老是在做用域鏈的末端) (這也是鏈式結構的特色) 綜上述:應該儘可能多的訪問局部變量。 經驗法則:若是某個跨做用域的值在函數中被引用一次以上,則把他存儲到局部變量

    2.3 改變做用域鏈

    有兩個語句能夠在執行時,臨時改變做用域鏈。

    第一個:with

    • 相似功能一般用來避免書寫重複代碼。
    • 給對象的全部屬性建立了一個變量。
    • 而且把該對象添加到做用域鏈頂,訪問該屬性速度變快,可是訪問其餘的變慢
    • 通常來講若是屢次訪問document,能夠吧document付給局部變量便可大大提高性能
    • with應該儘可能避免使用

· 第二個:try...catch

  • 當進入catch時,會將異常對象推入做用域鏈的頂部

    2.4 動態做用域

    with、try...catch、eval()都被認爲是動態做用域鏈,動態做用域只存在於代碼執行過程當中,所以沒法經過靜態分析。

    2.5 閉包、做用域和內存

    閉包:js最強的特性之一,函數訪問局部做用域以外的數據,使用閉包可能會致使性能問題,由於閉包函數阻礙了函數被正常回收,由於閉包有本身的做用域鏈,而且指向跟函數的做用域鏈同樣。 當閉包代碼執行時,會建立一個執行環境。

    在這裏插入圖片描述
    ==閉包代碼中訪問的屬性的位置不在第一層,這就是使用閉包最需關注的性能點==;在頻繁訪問跨做用域的標誌符時,每次都有性能損失。(閉包同時關係到內存和執行速度)

    2.6 對象成員

    訪問成員比訪問字面量或變量要慢,爲了理解其中的緣由,有必要先了解JS的對象本質。

    2.7 原型

    js的對象基於原型,他定義並實現了一個新建立的對象,所必須包含的成員列表。 對象經過一個內部屬性幫到他的原型,在firefox、safari、chrome瀏覽器中,這個屬性_proto_對開發者可見,而其餘瀏覽器確不容許腳本訪問此屬性。

    小結

    • 在JS中,數據存儲的位置對代碼總體性能產生重大的影響。
    • 訪問字面量和局部變量速度最快,相反,訪問數組元素和對象成員相對較慢
    • 因爲局部變量在鏈頂位置,因此更快
    • 全局變量在鏈尾位置,因此更慢
    • 減小嵌套對象
    • 一般來講,把經常使用的對象成員,數組元素,跨域變量保存到局部變量中可改變JS的性能
    • 多用局部變量

不知不覺寫到這裏已經挺長的了,因此決定留到下一篇文章吧。 若是以爲對你有幫助,就點個喜歡吧!

==支持一下,無限的動力^_^==

  • 加載和執行
  • 數據存取
  • DOM編程
  • 算法和流程控制
  • 快速響應的用戶界面
  • Ajax
  • 編程實戰
  • 構建並部署高性能的JavaScript應用
相關文章
相關標籤/搜索