第十一章:WEB浏览器中的javascript
客戶端javascript涵蓋在本系列的第二部分第10章,主要講解javascript是如何在web瀏覽器中實現的,這些章節介紹了大量的腳本宿主對象,這些對象可以表示瀏覽器窗口、文檔樹的內容。這些章節同樣涵蓋重要的web應用所需要的網絡編程API、本地存儲和檢索數據、畫圖等。主要包含內容有以下章節:
web瀏覽器中的javascript / window對象 / ?腳本化文檔 / ?腳本化css / 事件處理 / 校本化http / jQuery類庫 / 客戶端存儲 ?/ ?多媒體和圖形編程 / HTML5API
本書的第一部分介紹了javascript語言核心,第二部分開始轉向web瀏覽器中的javascript討論。迄今為止,我們的大部分例子是合法的javascript代碼,帶是沒有特定的上下文,也就是說它們運行在不明的環境總。本章節提供了一個可以允許javascript上下文。
在開始討論javascript之前,考慮下web瀏覽器是怎么呈現頁面的,其靜態頁面稱為文檔(document),相對于文檔來說,洽談web頁面甘江更像是應用。如果需要的話,這些頁面可以載入新的新想,因此看起來圖形化,而非文本化,并且它們可以進行離線操作,以及保存數據倒本地,以便再次訪問進行狀態恢復。此外,還有其它web頁面處于文檔和應用的中間,結合了兩者的特性。
本章以客戶端javascript概述開始,包括一個簡單的例子,以及對javascript如何在web文檔和web應用中角色討論。概述內容還介紹了那些內容會在后續章節中提到,接下來會詳細解釋javascript代碼在html文檔中如何嵌入,然后介紹兼容性、可訪問性和安全性等問題。
1.客戶端javascript
window對象是所有客戶端javascript特性和API的主要接入點。它表示web瀏覽器的一個窗口或窗體,并且可以用window表示來引用它。window對象定義了一些屬性,比如:Location對象的location屬性,Location對象指定當前顯示在窗口的URL,并允許腳本往窗口里載入新的URL。
//設置location屬性,跳轉至新的頁面window.location.href = "http://www.baidu.com"//location.href = "http://www.baidu.com"window對象還定義了一些方法,比如alert(),可以彈出一個對話框用來顯示一些信息,還有setTimeout(),可以注冊一個函數,在給定的一些時間內觸發一個回調
setTimeout(function(){alert("5秒跳轉"),1000});setTimeout(function(){location.href = "http://www.baidu.com"},5000)注意上面的代碼并沒有顯式的使用window 屬性。在客戶端javascript中,window對象 也是全局對象。這意味著window對象處于作用域鏈頂部,它的屬性和方法實際上是全局變量和全局函數。window對象有一個自身引用的屬性,叫做window。如果需要引用窗口對象本身,引用引用這個屬性。但是如果只想引用全局窗口對象的屬性,通常不需要用到window。
window對象還定義了很多其他重要的屬性、方法和構造函數,參見12章查看完整細節。
window對象中一個重要的屬性是document,它引用Document對象,后者表示顯示在窗口中的文檔。Document有一些重要的方法,比如getElementByid(),可以基于元素的id返回單一的文檔元素,(表示html標簽的一對開始/結束標記,以及它們之間所有的內容):
//查找id="timestamp"元素var timestamp = document.getElementById("timestamp")getElementById()返回的Element對象有其它重要的屬性和方法,比如允許腳本獲取它的內容,設置屬性值等:
//如果元素為空,往里邊插入的哪個區日期和事件if (timestamp.firstChild == null)timestamp.appendChild(document.createTextNode(new Date().toString()));查詢、遍歷和修改文檔將在12章做介紹。
每個Element對象都有style和className屬性,允許腳本指定元素css樣式,或修改元素上的css類名,設置這些css相關的屬性會改變文檔元素的呈現:
timestamp.style.backgroundColor="red";指定樣式className
//或者修改類,讓樣式指定具體內容timestamp.className = "heightlight TopDarkNav"14章會講解style和className屬性和其它css編程技術
window、Document和Element對象上另一個重要的屬性集合是事件處理程序相關的屬性。可以在腳本中為止綁定一個函數,這個函數會在某個事件發生時以異步的方式調用。
事件處理程序可以讓javascript代碼修改窗口,文檔和組成文檔的元素的行為。事件處理程序是以單詞"on"開始的,用法如下:
//當用戶單擊元素時,更新它的內容timestamp.onclick = function(){this.innerHTML = new Date().toDateString();}window對象的onload對象處理程序是最重要的事件處理程序之一。當顯示在文檔內的內容文檔且可以操作時觸發。javascript代碼通常封裝在onload事件處理程序里。15章會詳細講述事件。
下面的例子是onload處理程序的演示,并展示了客戶端javascript的實例代碼,包括查詢文檔元素,修改css類和定義事件處理程序。注意代碼里的函數是在另一個函數里定義的。因為事件處理程序的廣泛使用,是的嵌套函數在客戶端javascript中非常普遍。
<head><meta charset="utf-8"><style type="text/css">.reveal * {display: none;}.reveal *.handle {display: block;}</style><script type="text/javascript">window.onload = function() { //所有頁面邏輯加載完畢后啟動var element = document.getElementsByClassName("reveal");for (var i = 0; i < element.length; i++) { //對每個元素進行遍歷var elt = element[i];//找到容器中的“handle”元素var title = elt.getElementsByClassName("handle")[0];addRevealHandler(title, elt);console.log(elt.className);}function addRevealHandler(title, elt) {title.onclick = function() {if (elt.className == "reveal")elt.className = "revealed";else if (elt.className == "revealed")elt.className = "reveal";}}};</script></head><body><div class="reveal"><h1 class="handle">文字title(Click Here)</h1><p>文字內容</p></div></body>在本章的概要介紹到了,一些web頁面感覺上像文檔,而另一些則像應用。接下來的兩節探討javascript在兩種頁面類型里是如何使用的
i.web文檔里的javascript
javascript程序可以通過Document對象和它包含的Element對象遍歷和管理文檔內容。它可以通過操作css樣式和類,修改文章內容的呈現。并且可以通過注冊事件的處理辰星來定義文檔的元素行為。內容、呈現和行為的組合叫動態HTML或者DHTML,會在13-17章里介紹到
javascript可以增強用戶的體驗:比如以下方式:
- 創建動畫和其它視覺效果,巧妙地引導和幫助用戶進行頁面導航。
- 對表格進行分組,讓用戶更容易找到所需
- 隱藏某些內容,當用戶“深入”到內容時,逐漸展示詳細信息。
ii.web應用里的javascript
在web文檔庫使用的javascript DHTML特性在web應用里都會用到,對于web應用來說,除了內容、呈現和操作api之外,還依賴web瀏覽器環境提供更基礎的服務。‘
要真正的了解web應用,需要先認識web瀏覽器已經有很好的發展了,現在不僅僅是顯示溫度的角色了,而已經變成簡易的操作系統。操作系統定義很多底層的API、提供繪制圖形,保存文件等功能。web瀏覽器也定義了底層API(16章)、保存數據(18章),和繪制圖形(19章)。
謹記web瀏覽器是簡單的操作系統的概念,這樣就可以把web定義問用javascript訪問更多瀏覽器提供的高級服務(比如網絡、圖形和數據存儲)的web頁面。高級服務里最著名的是XMLHTTPRequest,可以對HTTP請求編程來啟動網絡。web里是固體這個從服務器獲取新信息,而不用從新載入頁面。類似這樣的web應用通常膠Ajax應用,Ajax構成了web2.0的脊梁。XMLHTTPRequest會在16章詳細介紹。
HTML5標準和相關標準為web應用定義了很多其他重要的API,如地理位置信息,歷史管理和后臺線程。使用這些API后,會開啟一場web應用的功能革命。這些內容在20章會介紹。
當然,javascript在web里的應用比在文檔里顯得更重要。javascript增強了web文檔。但是良好的設計的文檔需要在禁用了javascript后還能繼續工作。web應用的本質就是javascript程序。
2.在html嵌入javascript
- 內聯 <script></script>之間
- 放置在 <script>標簽的src屬性指定的文件中
- html事件處理程序中,例如onclick和onmouseover這樣的HTML屬性值指定。
- 放在一個URL里,這個URL使用特殊的"javascript:"協議
接下來小節會介紹4中javascript嵌套技術。但是,值得注意的是,html事情處理程序屬性和javascript:url這兩種方式現代的javascript代碼里已經很少使用。內聯腳本(沒有src)也比以前用的更少了。主張內容(html)和行為(javascript)代碼應該盡量保持分離。根據這個編程哲學,javascript最好通過<script>的src屬性嵌入到html文檔里
i.<script>元素
<script>//javascript代碼</script>在XHTML中<script>標簽的內容被當做其它內容一樣對待。如果javascript代碼包含了"<"或"&"字符,那么這些字符會被解釋成xml標記,因此,如果使用XHTML,最好把所有的javascript代碼放到一個CDATA部分里
<script><![CDATA[//這里是javascript代碼]]></script>下面的例子展示了一個簡單的javascript程序。注釋解釋了這個辰星是做什么的。主要演示javascript代碼以及css樣式表是如何嵌入到html文件里。
<script type="text/javascript">//定義一個函數顯式當前時間function displayTime() {var elt = document.getElementById("clock");var now = new Date();elt.innerHTML = now.toLocaleTimeString(); //顯式它setTimeout(displayTime, 1000);}window.onload = displayTime;</script><body><div id="clock"></div></body>ii.外部文件中的腳本
外部文件中的腳本它的用法如下:
<script src="unit.js"></script>javascript文件一般以.js結尾,它包含純粹的javascript代碼
使用src屬性時,<script></script>標簽之間的任何內容都會忽略掉,如果需要,可以在此處不錯說明文檔和版權信息,但要注意,如果有任何非空格或javascript的注釋文本出現在此,html5校驗器會報錯。
我們通常看到以下代碼
<script src="unit.js">config = {...};</script>這段戴拿定義了一些配置項,有unit.js來讀取,這是一種將頁面傳入庫文件的方法,在javascript庫中的開發中十分常見,其中<script></script>之間是一段純文本,在unit.js讀取時這段文本然后執行一次,瀏覽器不會自動執行script>中的代碼。
下面是一些使用src屬性的javascript的優點
- 可以把大塊的javascript代碼從HTML文件中刪除,這有助于保持內容和行為的分離
- 如果多個javascript共有相同的javascript代碼,用src屬性的方式可以讓你只管理一份代碼,而不用再代碼改變時而編輯每個HTML文件。
- 如果一個javascript代碼文件是多個頁面共享,那么只需下載一次,通過使用它的第一個頁面,隨后是頁面可以從瀏覽器緩存檢索它。
- 由于src是任意的url,因此來自一個web服務器的javascript程序或web頁面可以使用另一個web服務器輸出的代碼。很多互聯網廣告依賴于此。
- 從其他網站載入腳本的能力,可以讓我們更好的利用緩存(CDN方式)。
我們通常看到以下代碼
從服務器之外的服務器里載入腳本有重要的安全隱患,6.ii節介紹的同源安全策略會阻止一個域文檔的javascript和另外一個域的內容進行交互。但是,要注意和腳本本身的來源并沒有關系,而是和腳本嵌入的文檔來源有關系。因此,同源策略和并不適合用在以下的情況,即便代碼和文檔有不同的來源,javascript代碼也可以和它嵌入的文檔進行交互,當在頁面中用src腳本時,就給了腳本作者(這段腳本域的網站管理員)完全控制web頁面的權限。
iii.腳本類型
javascript是web元素腳本語言,而在默認的情況下,假定<script></script>包含或引用javascript代碼,如果使用不標準腳本語言,就必須用type指定MIME類型:
type的默認屬性是"text/javascript",如果需要,可以顯式的指定此類型,但完全沒必要。老的瀏覽器在標記上用language代替type標記,這樣的情況現在偶爾也看到。language屬性已經廢除,不應該再使用了
當web瀏覽器遇到<script></script>元素,并且當這個元素里包含其值不被瀏覽器識別的type屬性時,它會解析這個元素但不會嘗試顯示或者執行它的內容。這意味著可以使用<script>元素來嵌入任意文本數據倒文檔里,只要用type屬性聲明一個不可執行的類型。要獲取數據,可以屬于script元素(13章會介紹如果獲取這些元素)HTMLElement對象的text屬性。但是,要注意這些數據嵌入只對內聯腳本生效(steven souder著名的Controljs就是利用這個特性來控制代碼的執行。)如果src屬性和一個未知的類型。這個腳本會被忽略。并且不會從url下載任何內容。
iiii.HTML中的事件處理程序
當腳本所在的HTML文件被載入瀏覽器時,這個腳本里的javascript代碼只會執行一次。為了可交互,javascript程序必須定義事件處理程序,web瀏覽器先注冊javascript函數,并且在之后調用它作為事件的相應(比如用戶輸入)。正如本章開始例子展示的,javascript代碼可以通過把函數賦值給Element對象的屬性(比如onclick或onmoseover)來注冊事件處理程序。(還有其它注冊事件程序的方法,參見15章),這個Element對象表示文檔里的一個HTML元素。
類似onclick的事情處理程序屬性,用相同的名字對應到HTML屬性,并且還可以通過將javascript代碼放置在HTML屬性里來定義事件處理程序。例如:要定義用戶切換表單中的復選框調用的事件處理程序,可以作為表示復選框的html元素的屬性指定處理程序的代碼:
<input type="checkbox" name="options" value="gifwrap" οnchange="order.options.giftwarp = this.checked" />這里的onchangge屬性比較有意思,這個屬性值里的javascript代碼會在用戶選擇或取消選擇復選框時執行。
HTML中定義的事件處理程序的屬性可以包含任意挑javascript語句,相互之間用逗號分隔。這些語句組成一個函數體,然后這個函數稱為對于事件處理程序屬性的值。(15.2.ii會詳細介紹HTML屬性文本定義到javascript函數的轉換。)但是,通常HTML事件處理程序的屬性有類似上面的簡單賦值或定義在其它地方的簡單函數調用組成。這樣可以保持大部分實際的javascript代碼在腳本里,而不用把javascript和html混在一起。實際上,很多web開發者認為使用HTML事件處理程序是不好的習慣,他們更喜歡保持內容和行為的分離。
iiiii.URL中的javascript
在URL后面跟一個javascript:協議限定符,是另外一種javascript代碼到客戶端的方式。這種特殊的協議類型指定URL內容為任意字符串,這個字符串會被javascript解釋器運行的javascript代碼。它被當做單獨的一行代碼對待,這意味著語句之間必須用分號隔開,而//注釋必須用/**/注釋代替。javascript:URL能是不“資源”是轉換成字符串的執行代碼的返回值。如果代碼返回undefiend,那么這個資源是沒有內容的。
javascript:url可以用在可以使用常規URL的任意地方:比如<a>標記的href屬性,<form>的action屬性,甚至window.open()方法的參數。超鏈接里的javascript url可以是這樣的。
<a href="javascript:new Date().toLocaleTimeString()">what time it is</a>部分瀏覽器(比如firefox)會執行URL里的代碼,并使返回的字符串作為待顯新文章的內容。就像單擊一個超鏈接。瀏覽器會擦除當前文檔并顯示新文檔。其它瀏覽器(比如chrome和safari)不允許URL像上面一樣覆蓋當前文檔。但是,這樣的url還是支持的
<a href="javascript:alert(new Date().toLocaleTimeString())">what time it is</a>//檢查時間,而不覆蓋整個文檔部分瀏覽器載入這種類型的URL時,它會執行javascript代碼,但是由于沒有返回值(alert()方法返回undefined),作為新的文檔顯示內容。類似firefox的瀏覽器并不會替換當前顯示的文檔。要確保javascript:void不會替換當前的文檔,可以用void操作符強制函數調用或給表達式賦予undefined值。
<a href="javascript:void window.open('http://www.baidu.com')">baidu</a>和html事件處理程序一樣,javascript:url也是web早期的產物。通常避免在現代的網頁中使用。但javascript:url在html文檔之外確實有著重要的角色。如果要測試一段短javascript代碼,那么可以在瀏覽器地址欄里輸入javascript:URL,下面會介紹javascript:URL另外一個正統且強大的用法:瀏覽器書簽。
3.javascript里的程序的執行
客戶端javascript沒有嚴格的定義,我們可以說javascript程序是由web頁面中所包含的所有的javascript代碼。所有的代碼共用一個全局window對象。這意味著它們可以看到相同的Document對象,可以共享全局變量或函數,那么這個變量或函數會在腳本執行之后對任意javascript可見。
如果一個頁面包含嵌入窗體(通常使用<iframe>),嵌入的javascript和被嵌入的javascript代碼會有不同的全局對象,它可以看做一個單獨的javascript程序。但是,要記住,沒有嚴格關于javascript程序范圍的定義。如果外邊和里邊的文檔來自于同一個服務器,那么兩個文檔中的代碼就可以進行交互,并且如果你愿意,就可以把他們當做同一個程序的兩個相互作用的部分。12.8.iii會詳細介紹window對象以及不同窗體之間的交互。
javascript程序的執行有兩個階段。在第一個階段,載入文檔內容,并執行<script>元素的代碼(包括內陸腳本和外部腳本)。腳本通常按照它們在文檔中出現的順序執行。所有腳本里的代碼都是從上往下,按照它在條件、循環以及其他控制語句中出現的順序執行。
第二個階段,當文檔載入,所有腳本執行完成后,javascript就進入第二個階段。這個階段是異步的,而且是由事件驅動的。在時間驅動階段,web瀏覽器調用處理程序函數(由第一階段里執行的腳本指定的html事件處理程序,或之前調用的時間處理程序來定義),來響應時間異步事件的發生。調用事件處理程序通常是響應用戶輸入(如鼠標單擊,鍵盤按下)。但是還可以由網絡活動、運行時間、或者javascript代碼中的錯誤來觸發。15章會詳細介紹事件和事件處理程序。本章2.ii節也會進行更多的討論。注意,嵌入在web頁面里的javascript:URL也可以當做一種事件處理的程序,直到用戶單擊或者提交表單之后才會有效果。
事件在驅動階段第一個發生的事件是load事件,表示文檔已經完全載入,并可以操作。javascript經常通過這個事件來觸發或發送消息。
我們會經常看到一些定義函數的腳本程序,除了定義一個onload事件處理函數外不做其它操作,這個函數會在腳本事件驅動階段開始時被load觸發。正是這個onload事件會對文檔進行操作,并做程序想做的任何事。javascript程序的載入是短暫的,通常持續1到2秒,在文檔載入完成之后,事件驅動階段就會一直持續下去。因為這個階段是異步和事件驅動的,所以可能有長時間處于不活動狀態。沒有javascript被執行,被用戶或網絡事件觸發的活動打斷。本章3.iiii javascript執行的兩個階段。
核心javascript和客戶端javascript都有一個單線程執行模型。腳本和事件處理程序(無論如何)在同一個時間里只能執行一個,并沒有并發性。這保持了javascript編程的簡單性。本章3.iii會做介紹。
i.同步、異步 或延遲的腳本
javascript第一次添加到web瀏覽器時,還沒有api可以用來遍歷和操作文檔的結構內容,當文檔還在載入時,javascript唯一方法就是快速生成內容。它使用document.write()完成上述內容,下面就是1996最先進的javascript的代碼的樣子:
function factorials(n){ //用來計算階乘的函數if(n<=1) return n;else return n*factorials(n-1);}document.write("<table>"); document.write("<tr><th>n</th><th>n!</th></tr>"); //輸出表頭for(var i = 1; i<10;i++){ //輸出10行document.write("<tr><td>"+ i +"</td><td>" + factorials(i) +"</td></tr>");}document.write("</table>");當腳本把文本傳遞給document.write()時,這個文本被添加到文檔輸入流中,html解析器會在當前位置創建一個文本節點,將文本插入到這個文本節點后面。并不推薦使用document.write(),但在某些場景下有很重要的用途(13.10.ii節)。當HTML解析器遇到<script>元素時,它默認必須先執行腳本,然后再恢復文檔的解析和渲染。這對于內聯腳本沒有什么問題,但如果在javascript具有src屬性指定外部屬性指定外部條件,這意味著腳本后面的文檔部分在下載和執行腳本之前,都不會出現在瀏覽器中(所謂的“不出現在瀏覽器中”是指文檔的文本內容已經載入,但是并未被瀏覽器引擎解析為DOM樹,而DOM樹的生成是受javascript代碼“阻塞”頁面UI的渲染)。
腳本的的執行只在默認的情況下是同步和阻塞的。<script>標簽可以有defer和async屬性,這可以改變腳本的執行方式。這些都是布爾屬性,沒有值;只需要出現在<script>標簽里即可。HTML5說這些屬性只在Src屬性聯合使用時才能有作用,但有些瀏覽器還支持內聯的腳本。
<script defer src="1.js"></script><script async src="1.js"></script>defer和async屬性都像在告訴瀏覽器鏈接進來的腳本不會使用document.write(),也不會生成文檔內容,因此瀏覽器可以在下載腳本時繼續解析和渲染文檔。defer屬性是使得的瀏覽器延遲腳本的執行,直到文本的載入和解析完成,并可以操作。async屬性使得瀏覽器可以盡快的執行腳本,而不用在下載腳本時阻塞文檔解析。如果<script>標簽同時有兩個屬性,同時支持兩者的瀏覽器會遵循async屬性并忽略defer屬性。
注意,延遲的腳本會按照它們在文檔里的出現順序執行。而異步腳本在它們載入后執行,這意味著它們可能會無序執行。
在不支持async的屬性的瀏覽器里,通過動態的創建<script>元素并把它插入到文檔中,來實現腳本的異步載入和執行。下面的例子中loadasync()函數完成了這個工作。13會介紹它使用的技術。
/*異步載入并執行腳本*///異步載入并執行一個指定url中的腳本function loadasync(url) {var head = document.getElementsByTagName("head")[0]; //找到<head>元素var s = document.createElement("script"); //創建一個<script>元素s.src = url; //設置其src屬性head.appendChild(s); //將預算插入head標簽中 }loadasync(11.js);loadasync(12.js);loadasync(13.js);注意這個loadasync()函數會動態的載入腳本-腳本載入到文檔中,成為正在執行的javascript程序的一部分,既不是通過web頁面內聯包含,也不算來自web頁面的靜態引用。
ii.事件驅動的javascript
在上面的factorials()函數展示了javascript程序是同步載入的程序:在頁面載入時開始執行,生成一些輸出,然后結束。這種類型的程序在今天已經不常見了。反之,我們通過注冊時間處理程序來寫程序。之后在注冊的事件發生時異步調用這些函數。例如,想要為常用操作啟用鍵盤快捷鍵的web應用會為鍵盤事件處理程序。甚至非交互的程序也使用事件。假如想要寫一個分析文檔結構并自動生成文檔內容的表格程序。程序不需要用戶輸入事件的事件處理程序,但它還是會注冊onload事件處理程序。這樣就知道文檔在什么時候載入完成并可以生成內容表格了。
事件和事件處理是15章的主題,但是這一節會提供一個快速概述。事件都有名字,比如click、change、load、mouseover、keypress、readystatechange,指示發生的事件的通用類型。事件還有目標,它是一個對象,并且事件就是在它上面發生的。當我們談論事件時,必須指定事件的類型(名字)和目標,比如一個單擊事件發生在HTMLbutton對象上,或者一個readystatechange事件發生在XMLHttpRequest對象上。
如果想要呈現響應一個事件,寫一個函數,叫做“事件處理程序”、“事件監聽器”、“回調”。然后注冊這個函數,這樣它就會在事件發生時調用它。正如前面提到的,這可以通過HTML屬性來完成,不鼓勵把javascript程序和HTML內容混淆在一起。反之,注冊事件處理程序最簡單的方法就是把javascript函數賦值給目標對象屬性,類似這樣寫代碼:
window.onload = function(){...};document.getElementById("xx").onclick = function(){...};function handleResponse(){...}request.onreadystatechange = handleResponse;注意,按照約定事件處理程序的屬性的名字是以“on”開始,后面跟著事件的名字。還要注意在上面的人和代碼里沒有函數調用:只是把函數本身賦值給這些屬性。
瀏覽器會在這些事件發生時調用,用事件進行異步編程經常會涉及到嵌套函數,也經常要在函數的函數里定義函數。
對于大部分瀏覽器事件來說,會把一個對象傳遞給事件處理程序作為參數,那個對象的屬性提供了事件的詳細信息。比如傳遞給單擊事件的對象,會有一個屬性說明那個按鈕被單擊。(在IE里,這些信息存儲在全局event對象里,而不是傳遞給處理程序的函數。)事件處理程序的返回值有時用指定函數是否處理了事件。以及阻止瀏覽器執行它默認會進行的各種操作。
有些事件的目標是文檔元素,它們經常往上傳遞給文檔樹,這個過程叫“冒泡”。例如,如果用戶在<button>元素上單擊鼠標,單擊事件就會在按鈕上觸發。如果注冊在按鈕上的函數沒有處理(并且冒泡停止)該事件。事件冒泡到按鈕嵌套的容器元素。這樣,任何注冊在元素上的單擊事件都會調用。
如果需要為一個事件注冊多個事件處理程序函數,或者如果想要寫一個可以安全注冊事件處理程序的代碼模塊,就算另一個模塊已經為相同的事件注冊了一個處理程序,也需要用到另一種事件處理程序注冊技術。大部分可以成為事件目標對象都有一個叫做addEventListaner()方法,允許注冊多個監聽器:
window.addEventListener("load",function(){...},false);request.addEventListener("readystatechange",function(){...},false);注意,這個函數的第一個參數是事件的名稱。雖然addEventListener()已經標準化超過了10年,而微軟目前只在IE9里實現了它。在IE8之前的瀏覽器中,必須使用一個相似的方法,叫做attachEvent():
window.attachEvent("onload",function(){...});在第15章會看到更多關于addEventListener()和attachEvent()內容。
客戶端javascript還使用異步通知類型,這些類型往往不是事件。如果設置window對象的onerror屬性為一個函數,會發生(參加12.6節)javascript錯誤(或者其它未捕獲的異常)時調用函數。還有setTimeout()和setInterval()函數(這些是window對象方法,因此是客戶端javascript的全局函數)會在指定的一段時間之后出發指定函數的調用。傳遞給setTimeout()的函數和真實時間處理程序的注冊不同,它們通常叫做“回調邏輯”而不是“處理程序”,但它們和時間處理程序一樣,也是異步的。參加12.1獲得更多關于setTimeout()和setInterval()函數的信息。
下面的例子演示了setTimeout()、addEventlistenter()和attachEvent()、定義一個onload()函數注冊在文檔載入完成時執行的函數。
/*當文檔載入時調用一個函數*///注冊函數f,當文檔載入時執行這個函數f//如果文檔已經載入完成,盡快以異步的方式執行它function onLoad(f) {if (onLoad.loaded) //如果文檔已經載入完成window.setTimeout(f, 0); //將f放入異步對了,并盡快執行它else if (window.addEventListener) //注冊事件的標準方法window.addEventListener("load", f, false);else if (window.attachEvent)window.attachEvent("onload", f);}//給onLoad設置一個標志,用來指定文檔是否已經載入完成onLoad.loaded = false;//注冊一個函數,當文檔載入完成時使用這個標志onLoad(function() {onLoad.loaded = true;});iii.客戶端javascript線程模型
javascript語言核心并不包含任何線程機制,并且客戶端javascript傳統上也沒有定義任何線程機制。html5定義了一種作為后臺線程的"webworker",但是客戶端javascript還是像嚴格的單線程一樣工作。
單線程執行是為了讓編程更加簡單。編寫代碼時可以確保兩個事件處理程序不會同一時刻運行。操作文檔內容時不必擔心有其它線程試圖修改文檔。并且永遠不需要擔心javascript編寫時的鎖死,死鎖和竟態條件。
單線程執行意味這瀏覽器必須在腳本和事件語句程序執行時候停止響應用戶輸入。這為javascript程序員帶來了負擔。這意味這javascript腳本和事件處理程序不能運行太長事件。如果一個腳本執行計算密集的任務,它將會給文檔載入帶來延遲。如果事件程序執行計算密集任務,瀏覽器可能變得無法響應,可能導致用戶認為瀏覽器奔潰了。
如果程序執行的不太多計算導致明顯的延遲,在文檔沒有完全載入前,可以告知用戶正在運行計算并且瀏覽器沒有掛起。如果有可能可以將其分解為離散子任務。可以使用setTimeout()和setInterval()在后臺運行子任務。
HTML5定義了一種并發控制方式,“web worker”,它是一個用來執行計算密集任務而不凍結用戶界面的后臺線程。運行在web worker線程里的代碼不能訪問文檔里的內容,不能和主線程或其它worker共享狀態,只可以和主線程和其它worker通過異步事件進行通信,所以主線程不能檢測并非行,而且web worker不能修改javascript程序基礎單線程執行模型。20章4節會有更多相關內容。
?iiii.客戶端javascript時間線。
我們已經看到javascript程序從腳本執行階段開始,然后切換到事件處理階段。本節會詳細地解釋javascript程序執行的時間線:
這是一條理想的時間線(網友自己理解版本)
1、創建document對象,開始解析web頁面。創建HTMLHtmlElement對象,添加到document中。 創建HTMLHeadElement添加到HTMLHtmlElement中等等,總之遇到不同的標簽創建不同的element、node等等,這個階段document.readyState = 'loading'。2、遇到link外部css,創建線程加載,并繼續解析文檔。3、遇到script外部js,并且沒有設置async、defer,瀏覽器創建線程加載,并阻塞,等待js加載完成并執行該腳本,然后繼續解析文檔。4、遇到script外部js,并且設置有async、defter,瀏覽器創建線程加載,并繼續解析文檔。 對于async屬性的腳本,腳本加載完成后立即執行。 可以采用document.createElement('script')的方式動態插入script元素來模擬async屬性,實現腳本異步加載和執行。5、遇到img等,瀏覽器創建線程加載,并繼續解析文檔。6、當文檔解析完成,document.readyState = 'interactive'。7、文檔解析完成后,所有設置有defer的腳本會按照順序執行。(注意與async的不同)8、document對象觸發DOMContentLoaded事件,這也標志著程序執行從同步腳本執行階段,轉化為事件驅動階段。9、當所有async的腳本加載完成并執行后、img等加載完成后,document.readyState = 'complete',window對象觸發load事件。10、從此,以異步響應方式處理用戶輸入、網絡事件等。注:document的每一次readyState屬性變化,都會觸發readystatechange事件。但是所有的瀏覽器都沒有支持它的全部細節。所有的瀏覽器普遍支持load事件,都會觸發它。它是決定文檔完全載入并可操作的最通用技術。
DOMcontentLoaded事件在load事件之前觸發,當前所有的瀏覽器都支持這個事件,除了IE之外,document.readyState屬性已經被大部分瀏覽器實現。但是這個屬性在瀏覽器之間還存在差別。async屬性還不通用,使用上文中的loadasync()函數動態載入腳本的能力能讓程序的執行腳本載入階段和事件驅動之間界限更模糊。
這條時間線并沒有指定什么時候文檔開始對用戶可見或什么時候web瀏覽器必須開始響應用戶輸入事件。這些都是實現細節。對于很長的文檔或非常慢的網絡連接。web瀏覽器理論上會先渲染一部分文檔。并且在腳本執行之前,就能允許用戶和頁面產生一些交互。這種情況下,用戶輸入事件可能在程序執行的事件驅動開始之前觸發。
4.兼容性和互用性
web瀏覽器是web應用的操作系統,但是web是一個存在各種差異性的環境,web文檔和應用在不同的操作系統(windows、Mac OS、Linux、iPhone OS、Abdroid)不同開發商(microsoft、Mozilla、Apple、Google、Opera)的不同時代的瀏覽器(從預覽版到類似IE6這種十多年之前的瀏覽器)上查看和運行。能夠寫出一個健壯的javascript程序并能正確地運行在這么多類型的平臺上,的確是一種挑戰。
客戶端javascript兼容性和交互性的問題可以歸納為以下三個類:
演化:
web平臺一直在演變和發展當中。一個標準規范會倡導一個新的特性或API。一個新的特性看起來有用,瀏覽器開發商實現它,開發者開始使用這個特性。有一種情況是新的特性以及被添加到web中,新瀏覽器支持它但是老瀏覽器不支持。web開發者必先在使用老舊瀏覽器的大量用戶和使用新式瀏覽器的少量用戶之間做出權衡。
未實現:
舉例說明:IE8不支持<canvas>元素,雖然其他瀏覽器已經實現它了。一個更糟糕的例子是,Microsoft決定不實現DOM Level2 Event規范(它定義了addEventListener()和相關方法。。)這個規范在12年前就已經標準化了,其他瀏覽器廠商已經支持了很久了
bug:
每個瀏覽器都有bug,并且沒有按照規范準確地實現所有客戶端javascriptAPI,必須研究已有瀏覽器中的各種bug
幸運的是,javascript語言本身是被所有瀏覽器廠商實現的。它不是兼容性問題的源頭。在老式的瀏覽器ECMAScript3和新式的ECMAScript5之間轉換會導致兼容性問題,因為一些瀏覽器會支持嚴格模式而其他的不支持。瀏覽器廠商對ECMAScript5的實現是基本相互通用的。
首先,要解決javascript的兼容性的問題是要了解問題的根源是什么。web瀏覽器版本更迭更快。我們可以常去這些網站查詢信息:
MOzilla開發者中心
https://developer.mozilla.org
microsoft 開發者中心
https://msdn.microsoft.com/zh-cn/
apple開發者中心 safari
https://developer.apple.com/safari/tools/
Google Doctype
致力于幫助Web開發人員,目前尚處于Beta階段,其中已經包含多篇由Google頂級開發人員撰寫的關于網絡安全、網頁性能、JavaScript DOM處理、CSS技巧等方面的內容,可以作為Web開發者的參考資料庫
http://code.google.com/doctype/
http://a.deveria.com/caniuse/
這個“何時可用...”站點跟蹤重要web特性實現的狀態,允許根據各種標準進行過濾,并在某個特性只剩下少量已部署的瀏覽器不支持時推薦使用。
http://quirksmode.org/dom/
根據w3c標準列出各種瀏覽器的DOM兼容性表格
當然,意識到瀏覽器之間的兼容性問題只是第一步。接下來,你需要解決這些不兼容性。一種策略是限制之間使用你選擇支持的所有瀏覽器普遍支持的特性(或者很容易模擬出的特性)。之前提出的“何時可用...”這個網站就是圍繞這個策略的。它列出了ie6淘汰之后才能用的新特性。
下面介紹一個消極對付客戶端不兼容性問題的策略。
i.處理兼容性問題的類庫
處理不兼容問題其中一種最簡單的方法就是使用類庫。比如考慮圖像的<canvas>元素(19章主題)、IE(8)是唯一不支持這個特性的當前瀏覽器。在開源的"explerer canvas"項目上有一個類庫,引入一個javascript文件叫excanvas.js,然后IE就會看起來像支持<canvas>元素一樣。這個兼容類庫是一個很純粹的例子。
在開發過程中,可能會對某個特性編寫類似的庫。ECMAScript5數組方法(7.9節),比如forEach(),map()和reduce(),可以在ECMAScript3中完美模擬,并且通過把合適的類庫添加到頁面中,可以把這些強大有用 的方法當做所有瀏覽器平臺基線的部分。
但是有時候,不可能完全地(或有效地)在一個不支持某個特性的瀏覽器上實現一個特性,就像一件提到的,IE是唯一沒有實現標準事件處理API的瀏覽器,包括注冊事件處理程序addEventListener()方法。iE的attachEvent()不像addEventListener()一樣強大,并且在IE提供的繼承上透明地實現整個標準并非可行。反之,開發者要有一個折中的處理方法,通常叫addEvent,它可以用attachEvent()不像addEventListener()來方便實現綁定事件功能。然后,它們在所有的代碼里用addEvent()來代替addEventListener()和attachEvent()。
在實際開發工作中,今天不少web開發者在它們的頁面上使用看客戶端javascript框架。比如jQuery(17章)。使這些框架必不可少的一個重要功能是:它們定義了新的客戶端API并兼容所有瀏覽器。例如在jQuery中,事件處理程序的注冊是通過叫bind()方法完成的,如果你基于jQuery做web開發,就永遠不需要考慮addEventListener()和attachEvent()之間不兼容的問題。
ii.分級瀏覽器支持
分級瀏覽器支持(graded browser support)是由yahoo!率先提出的一種測試技術。分級瀏覽器中的A級要通過所需網頁完全可用,C級瀏覽器只需在HTML完整的情況下可用即可,而不需要javascript和css都正常工作,C級瀏覽器都稱作X級瀏覽器,這部分是全新或者罕見的瀏覽器。我們默認這些瀏覽器網頁是完全可用的。但官方不會對X級瀏覽器的功能提供完整的支持和測試。(11年第四季度統計,yahoo!已經不再將瀏覽器劃分為A級和C級。而是統一給出測試基準。根據這次更新 ,可以明顯感覺到測試基準向移動端傾斜)
?
iii.功能測試
功能測試(capability testing)是解決不兼容性問題的一種強大的計算。如果你想試用某個功能,但又不清楚這個功能是否在所有的瀏覽器中都有比較好的兼容性,則需要在腳本中添加相應的代碼來檢測是否在瀏覽器中支持該功能。如果期望使用的功能還沒有被當前的平臺所支持,要么不該在平臺中使用它,要么提供可在平臺上運行的代碼。
你將會在后面的各章節中一次又一次地看到功能體驗測試。例如在第15章,有如下面所示的代碼:
關于功能測試最重要的是,它并不涉及瀏覽器開發商和瀏覽器版本號,代碼在當前瀏覽器集合中有效,在瀏覽器后續的版本中也同樣有效,而不管后續的瀏覽器是否實現了這些功能集合。但要注意的是:這種方法需要測試某個屬性或方法是否在瀏覽器中已經定義了,出發該屬性或方法完全可用,如果Microsoft要定義一個addEventListener()方法,但Microsoft只是實現了一部分W3c規范,在調用addEventListener()之前這將會給使用特性的代碼帶來很多麻煩。
iiii.怪異模式和標準模式
Microsoft在發布IE6的時候,增加了IE5里沒有的很多css標準特性。但為了確保為了web內容的向后兼容性,它定義了兩種不同的渲染摩絲。在“標準模式”或“css兼容模式”中,瀏覽器要遵循css標準,在“怪異模式”中,瀏覽器表現的和IE4和IE5中的怪異非標準模式一樣,渲染模式的選擇依賴于html文件頂部的DOCTYPE聲明,在IE6中打開沒有DOCtype的頁面,會按照標準模式進行渲染。定義了html5 <!DOCTYPE HTML>的頁面在所有現代瀏覽器都會按照標準模式渲染。
怪異模式和標準模式之間的差別經歷了很長的發展歷程,現在新版的ie都支持標準模式。其它主流的瀏覽器都支持標準模式。這兩種模式都被html5規范所認可。怪異模式和標準模式之前的差異對于html和css開發者影響最大。但客戶端javascript代碼則需要知道文檔是以哪種模式進行渲染的。要進行這種渲染模式的特性檢測,通常檢測document.compatMode屬性。如果其值為"CSS1Compat",則說明瀏覽器告知在標準模式;如果其值為"BackCompat"(或undefined,說明屬性不存在),說明瀏覽器工作在怪異模式。所有現代的瀏覽器都實現了compatMode屬性,并且HTML5規范對它進行了標準化。
iiiii.瀏覽器測試
功能測試費用適用于檢測大小功能領域的支持,比如可以使用這種方法來確定瀏覽器是否支持w3c事件處理模式還是IE事件處理模型。另外,有時候可能需要在某種瀏覽器中解決個別BUG或難題,但缺沒有太好的方法來檢測bug的存在性。
在客戶端javascript中檢測瀏覽器的類型和版本的方法就是使用Navigator對象,我們將在12章學習它。在早期,客戶端嗅探就是一種常見的客戶端編程技術,現在的兼容性基本已經穩定。需要注意的是,客戶端嗅探可以在服務器端完成,web服務器根據User-Agent頭部可以選擇地返回特定的javascript代碼給客戶端。
5.可訪問性
web是發布信息的理想工具,而javascript程序可以增強對信息的訪問。然而,javascript程序員必須小心,因為程序員寫代碼太過隨意,以至于那行有視覺障礙或肢體困難的用戶沒辦法正確地獲取信息。
盲人用戶使用一種叫做屏幕閱讀器的“輔助性技術”將書面的文字轉換為語言詞匯。有些屏幕閱讀器是識別javascript的,而并一些只能在禁用javascript時才會工作得更好。javascript是的角色應當是增加信息的表現里,而不是負責信息的呈現。javascript可訪問性的一條重要元素則是,在禁用javascript解釋器的瀏覽器中也能正常使用(或至少某種形式能正常使用)。
可訪問性的另一個重要原則是,對于只使用鍵盤但不能(或者選擇不用)鼠標的用戶來說,如果編寫的javascript代碼依賴特定的鼠標事件。這就會給那行不使用鼠標的用戶排除在外。web瀏覽器允許使用鍵盤來遍歷和激活一個web頁面中的UI元素。并且javascript代碼也應該允許這樣做。正如15章所介紹,javascript支持獨立設備的事件:onfocus和onchange.以及依賴于設備的事件(onmouseover和onmousedown).為了考慮到可訪問性,應該盡早可能地支持獨立設備的事件。
創建可訪問的web頁面并非雞毛蒜皮的小事情。關心可訪問性的web應用開發應該閱讀這里的文檔http://www.w3.org/WAI/intro/aria。
6.安全性
web瀏覽器包含javascript解釋器,也就是說,一旦載入web頁面,就可以讓任意的javascript代碼在計算機里執行。很明顯,這里存在著安全隱患。瀏覽器廠商也在不斷權衡下面這兩個之前的博弈:
- 定義強大的客戶端API,啟用強大的WEB應用。
- 阻止惡意代碼讀取或修改數據、盜取隱私、詐騙或浪費時間。
就像在其它領域中一樣,javascript也在盤根錯節的安全漏洞和補丁之前不斷的發展變化。在web早期,瀏覽器添加了類似能夠打開、移動、調整窗口大小、已經編輯瀏覽器狀態欄的功能。由于廣告和詐騙的濫用,瀏覽器作者不得不限制和禁用這些API,今天在標準化的 html5中,瀏覽器廠商會小心(并且開放和合作性地)掂量某個長期存在的安全限制,并且在(希望)不引入新的安全漏洞的基礎上給客戶端javascript添加少量的功能。
下面幾節會介紹javascript的安全限制和安全問題,這些問題是每個web開發者都需要意識到的。
i.javascript不能做什么
web瀏覽器征對惡意代碼的第一條防線就是他們不支持某些功能。例如,客戶端javascript沒有權限來寫入或刪除客戶計算機上的任意文件或列出任意目錄。這意味著javascript不能刪除數據或植入病毒。(20.6.iiiii介紹javascript如何實現安全隱私文件系統,以及如何讀取和寫入文件。)
類似的客戶端javascript沒有任何通用的網絡能力。 ?客戶端javascript程序可以對HTTP協議編程(參見16章);并且html5有一個附屬標準膠webSockes,定義一個類套接字API,用于和指定的服務器通信。但是這些API都不允許對于范圍更廣的網絡進行直接訪問。通用的Iternet客戶端和服務器不能同時使用客戶端javascript來寫(這里的提示很重要,我們不能基于瀏覽器寫出一個“服務器”,網絡中的瀏覽器和瀏覽器之間無法直接通信。)
瀏覽器征對惡意代碼的第二條防線就是在自己支持某些功能上添加限制,以下是一些功能限制:
- javascript程序可以打開一個新的瀏覽器窗口,但是為了防止廣告商濫用彈出窗口,很多瀏覽器限制了這一功能,只有為了響應鼠標單擊這樣用戶觸發的時候才彈出,才能使用它。
- javascript程序可以關閉自己打開的瀏覽器窗口,但是不允許不經過用戶允許就關閉其他窗口。
- HTML fileupload元素的value屬性是只讀的。如果可以設置這個屬性,腳本就能設置它為任意期望的文件名,從而導致表單上傳指定文件。(比如密碼文件)內容到服務器。
- 腳本不能從不同的服務器(嚴格來說,這些服務器來自于不同的域,端口或協議,更詳細請參照本章6.ii)載入文檔的內容,除非這個就是包含腳本的文檔。類似地,一個腳本不能來自不同的服務器的文檔上注冊事件監聽。這就防止了腳本竊取其它頁面的用戶輸入(例如組成一個密碼項的鍵盤單擊過程),這一項限制叫同源策略,下一節將詳細介紹它。
注意這里并未列出所有客戶端javascript的限制項,不同瀏覽器有不同安全策略。并可能實現的API限制。部分瀏覽器還可能讓用戶偏好決定強弱的限制。
ii.同源策略
同源策略是對javascript代碼能夠操作那些WEB內容的一條完整的安全限制 。當web頁面使用多個<iframe>元素或者打開其它瀏覽器窗口的時候,這一策略通常就會發揮作用。在這種情況下,同源策略賦值管理窗口或窗體中的javascript代碼以及和其它窗口或幀的交互。具體來說,腳本只能讀取和所屬文檔來源相同的窗口和文檔的屬性(參見12章8節了解如何使用javascript操控多個窗口和窗體)。
文檔的來源包含協議、主機,以及載入文檔的URL端口。從不同的web服務器載入的文檔具有不同的來源。使用http:協議載入的文檔和使用https:載入的文檔具有不同的來源。即使他們來自同一個服務器。
腳本本身的來源和同源策略并不相關,相關的是腳本所嵌入的文檔的來源,理解這一點很重要。例如,一個來自于主機A的腳本被包含到宿主B的一個web頁面中,這個腳本的來源是主機B,并且可以完整的訪問包含它的文檔的內容。如果腳本打開一個新窗口并載入來自B主句的另一個文檔,腳本對這個文檔的內容也具有完全訪問權限。但是,如果腳本打開第三個窗口并載入一個來自主機C的文檔(或者來自主機A),這個同源策略就會發揮作用,阻止這個腳本訪問這個文檔。
實際上,同源策略并非應用不同源的窗口中所有對象的所有屬性。不過它應用到了其中大多數屬性,尤其是對Document對象的幾乎所有屬性而言。凡是包含另一個服務器中文檔的窗口或窗體,都是同源策略的適用范圍。如果腳本打開一個窗口,腳本也可以關閉它。但不能以任何方式查看窗口內部。同源策略還應用于XMLHttpRequests生成的HTTP請求(16章)。這個對象允許客戶端javascript生成任意的HTTP請求到腳本所屬文檔的web服務器。但是不允許腳本和其他web服務器通信。
對于防止腳本竊取有效的信息來說,同源策略是必須的。如果沒有這個限制。惡意腳本(通過防火墻載入安全的公司內外的瀏覽器)可能會打開一個空的窗口,欺騙用戶進入并使用這個窗口在網上瀏覽文件 。惡意腳本能夠讀取窗口的內容并將其發送回自己的服務器。同源策略防止了這種行為。
不嚴格的同源策略
在 某些情況下,同源策略就顯得稍微嚴格,本節會介紹三種不嚴格的同源策略
同源策略給那行使用多個子域的大站帶來了一些問題,例如來自a.ahthw.com的文檔里的腳本想要合法的從b.ahthw.com讀取文檔的屬性。為了支持這種類型多域名占占,可以使用Document.domain屬性。在默認的情況下,domain屬性存放的是載入文檔的服務器的主機名。可以設置這一屬性為ahthw.com
如果兩個窗口(或窗體)包含的腳本把domain設置成了相同的值,那么這兩個窗口就不再受同源策略的約束。他們可相互讀取對象的屬性。例如,從c.ahthw.com和d.ahthw.com載入的文檔的腳本可以把他們的document.domain屬性都設置為ahthw.com,這一依賴,這些文檔就有了同源性,可以相互讀取屬性。
不嚴格同源的第二項技術已經標準化為:跨域資源共享(Cross-Origin Resource Sharing,參見http://www.w3.org/TR/cors/)。使用“Origin”請求頭和新的Access-Control-Allow-Origin響應頭來擴展HTTP。它允許服務器用頭信息顯式地列出源,或使用通配符來匹配所有的源并允許任何地址請求文件。使用這種新的頭信息來允許跨域HTTP請求,這樣XMLHttpRequest就不會被同源策略所限制了。
另外一種新的技術,跨域文檔消息(cross - document messagin),允許來自一個文檔的腳本可以傳遞文本消息到另一個文檔的腳本,而不管腳本的來源是否不同。調用window對象上的 postMessage()方法,可以異步傳遞消息事件(可用onmessage事件句處理海曙來處理它)到窗口文檔里。一個文檔里的腳本還是不能調用在其他文檔里的方法和讀取屬性。但它們可以用這些消息傳遞技術來實現安全的通信(20章3節有跟多關于跨文檔消息api的細節)。
iii.跨站腳本
跨站腳本(cross-site scrpting),或者膠XSS,這個術語表示一類安全問題,也就是攻擊者想目標web站點諸如HTML標簽或者腳本。防止XSS攻擊是服務器端WEB開發者的一項基本規則。然而,客戶端javascript程序員也必須意識到或者能夠預防跨站腳本。
如果web頁面動態產生文檔內容,并且這些文檔內容是基于用戶提交數據的,而并沒有通過從中移除任何嵌入的html標簽來“消毒”的話,這個頁面就很容易遭到跨站腳本的攻擊。
來看一個小例子,考慮如下的web頁面,它使用javascript通過用戶名字像用戶說問好。
var name =decodeURIComponent(window.location.search.substring(1)) ||"";document.write("hello " + name)專門通過以下地址來調用
www.a.com/good.html?Davide這時候,它會顯示文本"Hello David"。但是考慮一下,使用下面的腳本調用會發生什么樣的情況。
www.a.com/good.html?%3Cscript%3Ealert("Davide")%3C/script%3E使用這個URL,腳本會動態的生成另一個腳本,(%3C和%3E是一個尖括號的編碼)在這個例子中,注入的腳本只顯示一個對話框。但是考慮如下情況
www.b.com/good.html?name=%3Cscript src=siteB/xxx.js%3E%3Cscript%3E之所以叫做跨站腳本估計,就是因為它涉及多個站點。站點B專門構造到站點A的鏈接,注入來自站點B的腳本。腳本xxx.js駐留在惡意站點B中,但現在,它嵌入到站點A中,并且可以對站點A的內容進行任何想要的操作。它可能損壞這個頁面或者使其不能正常工作(例如下節介紹的拒絕式服務攻擊)。者可能對站點A的用戶帶來不少壞處。
更危險的是,惡意腳本可以讀取站點A存儲的Cookie(可能統計數據或者其它個人驗證信息)然后發送回站點B。注入的腳本甚至可以誘騙用戶點擊將數據發送回站點B。
通常,防止XSS估計的方式是,在使用任何不可信的數據來動態創建文檔內容之前,從中移除HTML標簽。可以通過下一行代碼來移除<script>兩邊的尖括號。
上面簡單代碼把字符串中所有的尖括號替換成他們對應的HTML實體,也就是說將字符串中任意HTML標簽進行轉義過濾和刪除處理。IE8定義了一個更加微妙的toStaticHTML()方法,可以移除<script>標簽(和其它潛在的可執行內容)而不修改不可執行的HTML。toStaticHTML()是不標準的,但在javascript核心代碼中自己實現一個HTML安全函數也非常簡單。
(引用博客園作者小坦克:的預防提示)
1.將重要的cookie標記為http only, 這樣的話Javascript 中的document.cookie語句就不能獲取到cookie了.2.只允許用戶輸入我們期望的數據。 例如: 年齡的textbox中,只允許用戶4輸入數字。 而數字之外的字符都過濾掉。3.對數據進行Html Encode 處理4.過濾或移除特殊的Html標簽, 例如: <script>, <iframe> , < for <, > for >, " for5.過濾JavaScript 事件的標簽。例如 "οnclick=", "onfocus" 等等。 HTML5的內容安全策略則更進一步,它為<iframe>元素定義了一個sandbox。在實現之后,它允許顯示不可信的內容,并自動禁用腳本。
跨站腳本使有害的漏洞能夠立足web構架中,深入理解這些跨站腳本是值得的。很多在線資源可以參考
http://cert.org/historical/advisories/CA-2000-02.cfm
iiii.拒絕服務攻擊
這里描述同源策略和其他的安全限制可以很好地預防惡意代碼毀壞數據或者防止侵犯隱私這種問題。然而根據不止一種,拒絕服務攻擊,這種手法非常暴力。比如alert()對話框無限占用瀏覽器,或者使用一個沒有意義的循環來占用cpu等。
利用window.setInterval()方法占用cpu,并分配很多內存來根據你的系統。web瀏覽器沒有通用的辦法來放在這種笨重的手法。但是實際上沒有人會訪問一個濫用這種腳本的網站。因此在web上不是一個常見的問題。
7.客戶端框架。
一些web開發者基于客戶端框架或類庫創建它們的web應用非常便捷。從某種意義上來說,類庫也是框架。它們對web瀏覽器提供的標準和專用的API進行了封裝,向上提供更高級的API。
使用框架的好處就是可以使用更簡潔的代碼完成更復雜的功能,此外,完善的框架也會幫我們處理很多兼容性、安全性和可訪問性的問題。
17章會介紹jQuery,它是當前最流行的框架之一。理解底層的API會幫助你稱為更優秀的web開發者。雖說使用他們后很少使用原生的API。
除了jQuery外,還有很多優秀的javascript框架,其中有些框架非常有名,并且廣泛使用。
Prototype
Prototype類庫和jQuery類似,是專門征對DOM和AJax實現的一套工具,此外還問語言核心擴展了很多實用的工具,scriptaculous就是類庫基于Prototype實現的。
Dojo
DOjo是一個大型的框架,它包括一個種類繁多的UI組件集合、包括管理系統、數據抽象層等
YUI
YUI是yahoo使用的一個著名框架,YUI和Dojo一樣龐大,是一個無所不包的客戶端類庫,包含一眼工具、DOM、UI組件等。目前有兩個不兼容的版本YUI2和YUI3
Closure
Closure類庫是Google應用Gmail、Docs和其它web應用客戶端類庫。這個類庫是打算和Closure編譯器http://code.google.com/closure/compiler/配合使用的,剃除沒用的類庫函數。因為沒有用的代碼會在部署之前都被移除。Closure類庫設計者不需要保持特性集合的緊湊。所以Closure包含一個龐大的工具集。
GWT
GWT,即google web toolkit,是一個完全不同類型的客戶端框架。它用JAVA定義了web應用接口,并提供編譯器,將JAVA程序翻譯成兼容的客戶端Javascript。GWT在一些google產品中使用,但不如它們之間的Closure類庫用的那么廣泛。
(本文完,歡迎大家關注上章節內容:第十章:Javascript子集和擴展,下章內容:第十二章 window對象)
?
轉載于:https://www.cnblogs.com/ahthw/p/4299343.html
總結
以上是生活随笔為你收集整理的第十一章:WEB浏览器中的javascript的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 在一个Activity(
- 下一篇: 2015-04-22记录--一些JS疑惑