最近從新開始學習js,在第一章的一個小節裏寫到了「腳本調用策略」,書上寫的這部分很少,可是發如今我以前的(筆)面試中,問到的頻率仍是比較高的。本身一直習慣於直接把全部js文件寫在head裏,後來瞭解到優化後,會把js放在最底部,但並不太懂這樣作的好處,並且其餘的一些處理方式,本身也並未有過實際的操做。javascript
在面試中對這部分的考察,主要考察的是程序的性能方面。程序的性能是一個項目不斷地追求的,一般也是項目完成後須要長期作的一件事情,爲了讓用戶的體驗更好。性能優化的核心思想就是快,能夠預先準備數據(如緩存的使用),能夠按需獲取,能夠分段獲取等都是常見的優化手段。html
而對於js的優化(關於js的延遲加載)的好處是有助於提升頁面加載速度,js延遲加載就是等頁面加載完成以後在加載js文件。java
之因此要優化是由於HTML元素是按其在頁面中出現的次序調用的,若是用javascript來管理頁面上的元素(使用文檔對象模型dom),而且js加載於欲操做的HTML元素以前,則代碼將出錯。也就是說,咱們寫了js語句來獲取DOM對象,但因爲DOM結構尚未加載完成,所以獲取到的是空對象。jquery
示例:面試
<head> <script type="text/javascript"> var ul = document.getElementsByTagName('ul')[0]; //獲取ul var list = ul.getElementsByTagName('li'); for(var i =0;i<list.length;i++){ ul.appendChild(document.createElement('li')); } </script> </head> <body> <ul> <li>111</li> <li>222</li> <li>333</li> </ul> </body> </html>
上面的代碼猛一看好像沒啥問題,由於咱們老是很習慣這樣作,可是運行後發現控制檯報錯。瀏覽器
這就是由於js加載執行於DOM結構以前,因此獲取不到。簡單的解決辦法是把<script>放在<body>後面。緩存
同步加載,又稱阻塞模式,是咱們平時使用最多的方式,也就是直接將<script>寫在<head>裏。這種方式會阻止瀏覽器的後續處理,中止後續的解析,直到當前的加載完成。通常來講,同步加載是安全的,但若是咱們js裏設計到document內容輸出、獲取或修改DOM結構等行爲,就會產生頁面阻塞代碼出錯。因此通常就會建議把<script>寫在頁面最底部,以減小頁面阻塞。(這種方式可能也是咱們剛開始接觸到js優化,最常使用的一種方式。)安全
異步加載,又稱爲非阻塞加載,在瀏覽器下載執行js的同時,還會繼續後續頁面的處理。這裏也是通常面試會問到的一點,即js延遲加載的方式有哪些?性能優化
通常有六種方式;defer屬性、async屬性、動態建立dom方式、使用jquery的getScript方法、使用setTimeout延遲方法、讓js最後加載。app
寫的是六種方式,實際上本身在項目中真實用到的也就是讓js最後加載。因此對這所謂的六種方式,可能僅做爲一種知識儲備,當之後的項目有這種問題需求了,能夠有不一樣的解決思路。
HTML 4.01爲 <script>標籤訂義了defer屬性(延遲腳本的執行)。
其用途是:代表腳本在執行時不會影響頁面的構造,瀏覽器會當即下載,但延遲執行,即腳本會被延遲到整個頁面都解析完畢以後再執行。
defer屬性只適用於外部腳本文件,只有 Internet Explorer 支持 defer 屬性。
而且defer屬性解決了async引發的腳本順序問題(見async的缺點),使用defer屬性,腳本將按照在頁面中出現的順序加載和運行。
示例:
//腳本1 <script defer src="js/vendor/jquery.js"></script> //腳本2 <script defer src="js/script2.js"></script> //腳本3 <script defer src="js/script3.js"></script>
上述代碼添加 defer
屬性,腳本將按照在頁面中出現的順序加載,所以可確保腳本1一定加載於腳本2和 腳本3以前,同時腳本2一定加載於腳本3以前。
HTML 5爲 <script>標籤訂義了async屬性。添加此屬性後,腳本和HTML將一併加載(異步),代碼將順利運行。
瀏覽器遇到async腳本時不會阻塞頁面渲染,而是直接下載而後運行。但這樣的問題是,不一樣腳本運行次序就沒法控制,只是腳本不會阻止剩餘頁面的顯示。
async屬性只適用於外部腳本文件。
示例:
//腳本1 <script async src="js/vendor/jquery.js"></script> //腳本2 <script async src="js/script2.js"></script> //腳本3 <script async src="js/script3.js"></script>
上述代碼添加async 屬性,這三者的調用順序是不肯定的,腳本1能夠在腳本2和腳本3以前會以後調用,這是徹底不肯定的。若是腳本2和腳本3須要依賴腳本1中的函數,那麼不肯定的調用順序會致使錯誤。
因此,當頁面的不一樣腳本之間彼此獨立,且不依賴於本頁面的其餘任何腳本時,async是最理想的選擇。
相同:
區別:
async
。也就是每個async屬性的腳本都在它下載結束以後當即執行,同時會在window的load事件以前執行。defer
,將關聯的腳本按所需順序置於 HTML 中。//這些代碼應被放置在</body>標籤前(接近HTML文件底部) <script type="text/javascript"> function downloadJSAtOnload(){ var element = document.createElement("script"); element.src = "defer.js"; document.body.appendChild(element); } if (window.addEventListener) //添加監聽事件 window.addEventListener("load",downloadJSAtOnload,false); //事件在冒泡階段執行 else if (window.attachEvent) window.attachEvent("onload",downloadJSAtOnload); else window.onload =downloadJSAtOnload; </script>
getScript() 方法經過 HTTP GET 請求載入並執行 JavaScript 文件。
語法:
jQuery.getScript(url,success(response,status))
url(必寫):將要請求的 URL 字符串
success(response,status)(可選):規定請求成功後執行的回調函數。
其中的參數
response - 包含來自請求的結果數據
status - 包含請求的狀態("success", "notmodified", "error", "timeout" 或 "parsererror")
//加載並執行 test.js: $.getScript("test.js"); //加載並執行 test.js ,成功後顯示信息 $.getScript("test.js", function(){ alert("Script loaded and executed."); });
目的:延遲加載js代碼,給網頁加載留出時間
<script type="text/javascript"> function A(){ $.post("/lord/login",{name:username,pwd:password},function(){ alert("Hello World!"); }) } $(function (){ setTimeout("A()",1000); //延遲1秒 }) </script>
將腳本元素放在文檔體的低端,這樣腳本就能夠在HTML解析完畢後加載了。但此方案的問題是,只有在全部HTML DOM加載完成後纔開始腳本的加載/解析過程。對於有大量js代碼的大型網站,可能會帶來顯著的性能損耗。