日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

      歡迎訪問(wèn) 生活随笔!

      生活随笔

      當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

      javascript

      第十一章:WEB浏览器中的javascript

      發(fā)布時(shí)間:2025/7/14 javascript 19 豆豆
      生活随笔 收集整理的這篇文章主要介紹了 第十一章:WEB浏览器中的javascript 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

      客戶端javascript涵蓋在本系列的第二部分第10章,主要講解javascript是如何在web瀏覽器中實(shí)現(xiàn)的,這些章節(jié)介紹了大量的腳本宿主對(duì)象,這些對(duì)象可以表示瀏覽器窗口、文檔樹的內(nèi)容。這些章節(jié)同樣涵蓋重要的web應(yīng)用所需要的網(wǎng)絡(luò)編程API、本地存儲(chǔ)和檢索數(shù)據(jù)、畫圖等。主要包含內(nèi)容有以下章節(jié):

      web瀏覽器中的javascript / window對(duì)象 / ?腳本化文檔 / ?腳本化css / 事件處理 / 校本化http / jQuery類庫(kù) / 客戶端存儲(chǔ) ?/ ?多媒體和圖形編程 / HTML5API

      本書的第一部分介紹了javascript語(yǔ)言核心,第二部分開始轉(zhuǎn)向web瀏覽器中的javascript討論。迄今為止,我們的大部分例子是合法的javascript代碼,帶是沒有特定的上下文,也就是說(shuō)它們運(yùn)行在不明的環(huán)境總。本章節(jié)提供了一個(gè)可以允許javascript上下文。

      在開始討論javascript之前,考慮下web瀏覽器是怎么呈現(xiàn)頁(yè)面的,其靜態(tài)頁(yè)面稱為文檔(document),相對(duì)于文檔來(lái)說(shuō),洽談web頁(yè)面甘江更像是應(yīng)用。如果需要的話,這些頁(yè)面可以載入新的新想,因此看起來(lái)圖形化,而非文本化,并且它們可以進(jìn)行離線操作,以及保存數(shù)據(jù)倒本地,以便再次訪問(wèn)進(jìn)行狀態(tài)恢復(fù)。此外,還有其它web頁(yè)面處于文檔和應(yīng)用的中間,結(jié)合了兩者的特性。

      本章以客戶端javascript概述開始,包括一個(gè)簡(jiǎn)單的例子,以及對(duì)javascript如何在web文檔和web應(yīng)用中角色討論。概述內(nèi)容還介紹了那些內(nèi)容會(huì)在后續(xù)章節(jié)中提到,接下來(lái)會(huì)詳細(xì)解釋javascript代碼在html文檔中如何嵌入,然后介紹兼容性、可訪問(wèn)性和安全性等問(wèn)題。

      1.客戶端javascript

      window對(duì)象是所有客戶端javascript特性和API的主要接入點(diǎn)。它表示web瀏覽器的一個(gè)窗口或窗體,并且可以用window表示來(lái)引用它。window對(duì)象定義了一些屬性,比如:Location對(duì)象的location屬性,Location對(duì)象指定當(dāng)前顯示在窗口的URL,并允許腳本往窗口里載入新的URL。

      //設(shè)置location屬性,跳轉(zhuǎn)至新的頁(yè)面window.location.href = "http://www.baidu.com"//location.href = "http://www.baidu.com"

      window對(duì)象還定義了一些方法,比如alert(),可以彈出一個(gè)對(duì)話框用來(lái)顯示一些信息,還有setTimeout(),可以注冊(cè)一個(gè)函數(shù),在給定的一些時(shí)間內(nèi)觸發(fā)一個(gè)回調(diào)

      setTimeout(function(){alert("5秒跳轉(zhuǎn)"),1000});setTimeout(function(){location.href = "http://www.baidu.com"},5000)

      注意上面的代碼并沒有顯式的使用window 屬性。在客戶端javascript中,window對(duì)象 也是全局對(duì)象。這意味著window對(duì)象處于作用域鏈頂部,它的屬性和方法實(shí)際上是全局變量和全局函數(shù)。window對(duì)象有一個(gè)自身引用的屬性,叫做window。如果需要引用窗口對(duì)象本身,引用引用這個(gè)屬性。但是如果只想引用全局窗口對(duì)象的屬性,通常不需要用到window。

      window對(duì)象還定義了很多其他重要的屬性、方法和構(gòu)造函數(shù),參見12章查看完整細(xì)節(jié)

      window對(duì)象中一個(gè)重要的屬性是document,它引用Document對(duì)象,后者表示顯示在窗口中的文檔。Document有一些重要的方法,比如getElementByid(),可以基于元素的id返回單一的文檔元素,(表示html標(biāo)簽的一對(duì)開始/結(jié)束標(biāo)記,以及它們之間所有的內(nèi)容):

      //查找id="timestamp"元素var timestamp = document.getElementById("timestamp")

      getElementById()返回的Element對(duì)象有其它重要的屬性和方法,比如允許腳本獲取它的內(nèi)容,設(shè)置屬性值等:

      //如果元素為空,往里邊插入的哪個(gè)區(qū)日期和事件if (timestamp.firstChild == null)timestamp.appendChild(document.createTextNode(new Date().toString()));

      查詢、遍歷和修改文檔將在12章做介紹

      每個(gè)Element對(duì)象都有style和className屬性,允許腳本指定元素css樣式,或修改元素上的css類名,設(shè)置這些css相關(guān)的屬性會(huì)改變文檔元素的呈現(xiàn):

      timestamp.style.backgroundColor="red";

      指定樣式className

      //或者修改類,讓樣式指定具體內(nèi)容timestamp.className = "heightlight TopDarkNav"

      14章會(huì)講解style和className屬性和其它c(diǎn)ss編程技術(shù)

      window、Document和Element對(duì)象上另一個(gè)重要的屬性集合是事件處理程序相關(guān)的屬性。可以在腳本中為止綁定一個(gè)函數(shù),這個(gè)函數(shù)會(huì)在某個(gè)事件發(fā)生時(shí)以異步的方式調(diào)用。

      事件處理程序可以讓javascript代碼修改窗口,文檔和組成文檔的元素的行為。事件處理程序是以單詞"on"開始的,用法如下:

      //當(dāng)用戶單擊元素時(shí),更新它的內(nèi)容timestamp.onclick = function(){this.innerHTML = new Date().toDateString();}

      window對(duì)象的onload對(duì)象處理程序是最重要的事件處理程序之一。當(dāng)顯示在文檔內(nèi)的內(nèi)容文檔且可以操作時(shí)觸發(fā)。javascript代碼通常封裝在onload事件處理程序里。15章會(huì)詳細(xì)講述事件。

      下面的例子是onload處理程序的演示,并展示了客戶端javascript的實(shí)例代碼,包括查詢文檔元素,修改css類和定義事件處理程序。注意代碼里的函數(shù)是在另一個(gè)函數(shù)里定義的。因?yàn)槭录幚沓绦虻膹V泛使用,是的嵌套函數(shù)在客戶端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() { //所有頁(yè)面邏輯加載完畢后啟動(dòng)var element = document.getElementsByClassName("reveal");for (var i = 0; i < element.length; i++) { //對(duì)每個(gè)元素進(jìn)行遍歷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>文字內(nèi)容</p></div></body>

      在本章的概要介紹到了,一些web頁(yè)面感覺上像文檔,而另一些則像應(yīng)用。接下來(lái)的兩節(jié)探討javascript在兩種頁(yè)面類型里是如何使用的

      i.web文檔里的javascript

      javascript程序可以通過(guò)Document對(duì)象和它包含的Element對(duì)象遍歷和管理文檔內(nèi)容。它可以通過(guò)操作css樣式和類,修改文章內(nèi)容的呈現(xiàn)。并且可以通過(guò)注冊(cè)事件的處理辰星來(lái)定義文檔的元素行為。內(nèi)容、呈現(xiàn)和行為的組合叫動(dòng)態(tài)HTML或者DHTML,會(huì)在13-17章里介紹到

      javascript可以增強(qiáng)用戶的體驗(yàn):比如以下方式:

      • 創(chuàng)建動(dòng)畫和其它視覺效果,巧妙地引導(dǎo)和幫助用戶進(jìn)行頁(yè)面導(dǎo)航。
      • 對(duì)表格進(jìn)行分組,讓用戶更容易找到所需
      • 隱藏某些內(nèi)容,當(dāng)用戶“深入”到內(nèi)容時(shí),逐漸展示詳細(xì)信息。

      ii.web應(yīng)用里的javascript

      在web文檔庫(kù)使用的javascript DHTML特性在web應(yīng)用里都會(huì)用到,對(duì)于web應(yīng)用來(lái)說(shuō),除了內(nèi)容、呈現(xiàn)和操作api之外,還依賴web瀏覽器環(huán)境提供更基礎(chǔ)的服務(wù)。

      要真正的了解web應(yīng)用,需要先認(rèn)識(shí)web瀏覽器已經(jīng)有很好的發(fā)展了,現(xiàn)在不僅僅是顯示溫度的角色了,而已經(jīng)變成簡(jiǎn)易的操作系統(tǒng)。操作系統(tǒng)定義很多底層的API、提供繪制圖形,保存文件等功能。web瀏覽器也定義了底層API(16章)、保存數(shù)據(jù)(18章),和繪制圖形(19章)。

      謹(jǐn)記web瀏覽器是簡(jiǎn)單的操作系統(tǒng)的概念,這樣就可以把web定義問(wèn)用javascript訪問(wèn)更多瀏覽器提供的高級(jí)服務(wù)(比如網(wǎng)絡(luò)、圖形和數(shù)據(jù)存儲(chǔ))的web頁(yè)面。高級(jí)服務(wù)里最著名的是XMLHTTPRequest,可以對(duì)HTTP請(qǐng)求編程來(lái)啟動(dòng)網(wǎng)絡(luò)。web里是固體這個(gè)從服務(wù)器獲取新信息,而不用從新載入頁(yè)面。類似這樣的web應(yīng)用通常膠Ajax應(yīng)用,Ajax構(gòu)成了web2.0的脊梁。XMLHTTPRequest會(huì)在16章詳細(xì)介紹。

      HTML5標(biāo)準(zhǔn)和相關(guān)標(biāo)準(zhǔn)為web應(yīng)用定義了很多其他重要的API,如地理位置信息,歷史管理和后臺(tái)線程。使用這些API后,會(huì)開啟一場(chǎng)web應(yīng)用的功能革命。這些內(nèi)容在20章會(huì)介紹。
      當(dāng)然,javascript在web里的應(yīng)用比在文檔里顯得更重要。javascript增強(qiáng)了web文檔。但是良好的設(shè)計(jì)的文檔需要在禁用了javascript后還能繼續(xù)工作。web應(yīng)用的本質(zhì)就是javascript程序。

      2.在html嵌入javascript

      • 內(nèi)聯(lián) <script></script>之間
      • 放置在 <script>標(biāo)簽的src屬性指定的文件中
      • html事件處理程序中,例如onclick和onmouseover這樣的HTML屬性值指定。
      • 放在一個(gè)URL里,這個(gè)URL使用特殊的"javascript:"協(xié)議

      接下來(lái)小節(jié)會(huì)介紹4中javascript嵌套技術(shù)。但是,值得注意的是,html事情處理程序?qū)傩院蚸avascript:url這兩種方式現(xiàn)代的javascript代碼里已經(jīng)很少使用。內(nèi)聯(lián)腳本(沒有src)也比以前用的更少了。主張內(nèi)容(html)和行為(javascript)代碼應(yīng)該盡量保持分離。根據(jù)這個(gè)編程哲學(xué),javascript最好通過(guò)<script>的src屬性嵌入到html文檔里

      i.<script>元素

      <script>//javascript代碼</script>

      在XHTML中<script>標(biāo)簽的內(nèi)容被當(dāng)做其它內(nèi)容一樣對(duì)待。如果javascript代碼包含了"<"或"&"字符,那么這些字符會(huì)被解釋成xml標(biāo)記,因此,如果使用XHTML,最好把所有的javascript代碼放到一個(gè)CDATA部分里

      <script><![CDATA[//這里是javascript代碼]]></script>

      下面的例子展示了一個(gè)簡(jiǎn)單的javascript程序。注釋解釋了這個(gè)辰星是做什么的。主要演示javascript代碼以及css樣式表是如何嵌入到html文件里。

      <script type="text/javascript">//定義一個(gè)函數(shù)顯式當(dāng)前時(shí)間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結(jié)尾,它包含純粹的javascript代碼

      使用src屬性時(shí),<script></script>標(biāo)簽之間的任何內(nèi)容都會(huì)忽略掉,如果需要,可以在此處不錯(cuò)說(shuō)明文檔和版權(quán)信息,但要注意,如果有任何非空格或javascript的注釋文本出現(xiàn)在此,html5校驗(yàn)器會(huì)報(bào)錯(cuò)。

      我們通常看到以下代碼

      <script src="unit.js">config = {...};</script>

      這段戴拿定義了一些配置項(xiàng),有unit.js來(lái)讀取,這是一種將頁(yè)面?zhèn)魅霂?kù)文件的方法,在javascript庫(kù)中的開發(fā)中十分常見,其中<script></script>之間是一段純文本,在unit.js讀取時(shí)這段文本然后執(zhí)行一次,瀏覽器不會(huì)自動(dòng)執(zhí)行script>中的代碼

      下面是一些使用src屬性的javascript的優(yōu)點(diǎn)

      • 可以把大塊的javascript代碼從HTML文件中刪除,這有助于保持內(nèi)容和行為的分離
      • 如果多個(gè)javascript共有相同的javascript代碼,用src屬性的方式可以讓你只管理一份代碼,而不用再代碼改變時(shí)而編輯每個(gè)HTML文件。
      • 如果一個(gè)javascript代碼文件是多個(gè)頁(yè)面共享,那么只需下載一次,通過(guò)使用它的第一個(gè)頁(yè)面,隨后是頁(yè)面可以從瀏覽器緩存檢索它。
      • 由于src是任意的url,因此來(lái)自一個(gè)web服務(wù)器的javascript程序或web頁(yè)面可以使用另一個(gè)web服務(wù)器輸出的代碼。很多互聯(lián)網(wǎng)廣告依賴于此。
      • 從其他網(wǎng)站載入腳本的能力,可以讓我們更好的利用緩存(CDN方式)。

      我們通常看到以下代碼

      從服務(wù)器之外的服務(wù)器里載入腳本有重要的安全隱患,6.ii節(jié)介紹的同源安全策略會(huì)阻止一個(gè)域文檔的javascript和另外一個(gè)域的內(nèi)容進(jìn)行交互。但是,要注意和腳本本身的來(lái)源并沒有關(guān)系,而是和腳本嵌入的文檔來(lái)源有關(guān)系。因此,同源策略和并不適合用在以下的情況,即便代碼和文檔有不同的來(lái)源,javascript代碼也可以和它嵌入的文檔進(jìn)行交互,當(dāng)在頁(yè)面中用src腳本時(shí),就給了腳本作者(這段腳本域的網(wǎng)站管理員)完全控制web頁(yè)面的權(quán)限。

      iii.腳本類型
      javascript是web元素腳本語(yǔ)言,而在默認(rèn)的情況下,假定<script></script>包含或引用javascript代碼,如果使用不標(biāo)準(zhǔn)腳本語(yǔ)言,就必須用type指定MIME類型:

      <script type="text/vbscript">//這里是VBScript代碼</script>

      type的默認(rèn)屬性是"text/javascript",如果需要,可以顯式的指定此類型,但完全沒必要。老的瀏覽器在標(biāo)記上用language代替type標(biāo)記,這樣的情況現(xiàn)在偶爾也看到。language屬性已經(jīng)廢除,不應(yīng)該再使用了

      當(dāng)web瀏覽器遇到<script></script>元素,并且當(dāng)這個(gè)元素里包含其值不被瀏覽器識(shí)別的type屬性時(shí),它會(huì)解析這個(gè)元素但不會(huì)嘗試顯示或者執(zhí)行它的內(nèi)容。這意味著可以使用<script>元素來(lái)嵌入任意文本數(shù)據(jù)倒文檔里,只要用type屬性聲明一個(gè)不可執(zhí)行的類型。要獲取數(shù)據(jù),可以屬于script元素(13章會(huì)介紹如果獲取這些元素)HTMLElement對(duì)象的text屬性。但是,要注意這些數(shù)據(jù)嵌入只對(duì)內(nèi)聯(lián)腳本生效(steven souder著名的Controljs就是利用這個(gè)特性來(lái)控制代碼的執(zhí)行。)如果src屬性和一個(gè)未知的類型。這個(gè)腳本會(huì)被忽略。并且不會(huì)從url下載任何內(nèi)容。

      iiii.HTML中的事件處理程序

      當(dāng)腳本所在的HTML文件被載入瀏覽器時(shí),這個(gè)腳本里的javascript代碼只會(huì)執(zhí)行一次。為了可交互,javascript程序必須定義事件處理程序,web瀏覽器先注冊(cè)javascript函數(shù),并且在之后調(diào)用它作為事件的相應(yīng)(比如用戶輸入)。正如本章開始例子展示的,javascript代碼可以通過(guò)把函數(shù)賦值給Element對(duì)象的屬性(比如onclick或onmoseover)來(lái)注冊(cè)事件處理程序。(還有其它注冊(cè)事件程序的方法,參見15章),這個(gè)Element對(duì)象表示文檔里的一個(gè)HTML元素。

      類似onclick的事情處理程序?qū)傩?#xff0c;用相同的名字對(duì)應(yīng)到HTML屬性,并且還可以通過(guò)將javascript代碼放置在HTML屬性里來(lái)定義事件處理程序。例如:要定義用戶切換表單中的復(fù)選框調(diào)用的事件處理程序,可以作為表示復(fù)選框的html元素的屬性指定處理程序的代碼:

      <input type="checkbox" name="options" value="gifwrap" οnchange="order.options.giftwarp = this.checked" />

      這里的onchangge屬性比較有意思,這個(gè)屬性值里的javascript代碼會(huì)在用戶選擇或取消選擇復(fù)選框時(shí)執(zhí)行。

      HTML中定義的事件處理程序的屬性可以包含任意挑javascript語(yǔ)句,相互之間用逗號(hào)分隔。這些語(yǔ)句組成一個(gè)函數(shù)體,然后這個(gè)函數(shù)稱為對(duì)于事件處理程序?qū)傩缘闹怠?#xff08;15.2.ii會(huì)詳細(xì)介紹HTML屬性文本定義到j(luò)avascript函數(shù)的轉(zhuǎn)換。)但是,通常HTML事件處理程序的屬性有類似上面的簡(jiǎn)單賦值或定義在其它地方的簡(jiǎn)單函數(shù)調(diào)用組成。這樣可以保持大部分實(shí)際的javascript代碼在腳本里,而不用把javascript和html混在一起。實(shí)際上,很多web開發(fā)者認(rèn)為使用HTML事件處理程序是不好的習(xí)慣,他們更喜歡保持內(nèi)容和行為的分離。

      iiiii.URL中的javascript

      在URL后面跟一個(gè)javascript:協(xié)議限定符,是另外一種javascript代碼到客戶端的方式。這種特殊的協(xié)議類型指定URL內(nèi)容為任意字符串,這個(gè)字符串會(huì)被javascript解釋器運(yùn)行的javascript代碼。它被當(dāng)做單獨(dú)的一行代碼對(duì)待,這意味著語(yǔ)句之間必須用分號(hào)隔開,而//注釋必須用/**/注釋代替。javascript:URL能是不“資源”是轉(zhuǎn)換成字符串的執(zhí)行代碼的返回值。如果代碼返回undefiend,那么這個(gè)資源是沒有內(nèi)容的。

      javascript:url可以用在可以使用常規(guī)URL的任意地方:比如<a>標(biāo)記的href屬性,<form>的action屬性,甚至window.open()方法的參數(shù)。超鏈接里的javascript url可以是這樣的。

      <a href="javascript:new Date().toLocaleTimeString()">what time it is</a>

      部分瀏覽器(比如firefox)會(huì)執(zhí)行URL里的代碼,并使返回的字符串作為待顯新文章的內(nèi)容。就像單擊一個(gè)超鏈接。瀏覽器會(huì)擦除當(dāng)前文檔并顯示新文檔。其它瀏覽器(比如chrome和safari)不允許URL像上面一樣覆蓋當(dāng)前文檔。但是,這樣的url還是支持的

      <a href="javascript:alert(new Date().toLocaleTimeString())">what time it is</a>//檢查時(shí)間,而不覆蓋整個(gè)文檔

      部分瀏覽器載入這種類型的URL時(shí),它會(huì)執(zhí)行javascript代碼,但是由于沒有返回值(alert()方法返回undefined),作為新的文檔顯示內(nèi)容。類似firefox的瀏覽器并不會(huì)替換當(dāng)前顯示的文檔。要確保javascript:void不會(huì)替換當(dāng)前的文檔,可以用void操作符強(qiáng)制函數(shù)調(diào)用或給表達(dá)式賦予undefined值。

      <a href="javascript:void window.open('http://www.baidu.com')">baidu</a>

      和html事件處理程序一樣,javascript:url也是web早期的產(chǎn)物。通常避免在現(xiàn)代的網(wǎng)頁(yè)中使用。但javascript:url在html文檔之外確實(shí)有著重要的角色。如果要測(cè)試一段短javascript代碼,那么可以在瀏覽器地址欄里輸入javascript:URL,下面會(huì)介紹javascript:URL另外一個(gè)正統(tǒng)且強(qiáng)大的用法:瀏覽器書簽。

      3.javascript里的程序的執(zhí)行

      客戶端javascript沒有嚴(yán)格的定義,我們可以說(shuō)javascript程序是由web頁(yè)面中所包含的所有的javascript代碼。所有的代碼共用一個(gè)全局window對(duì)象。這意味著它們可以看到相同的Document對(duì)象,可以共享全局變量或函數(shù),那么這個(gè)變量或函數(shù)會(huì)在腳本執(zhí)行之后對(duì)任意javascript可見。

      如果一個(gè)頁(yè)面包含嵌入窗體(通常使用<iframe>),嵌入的javascript和被嵌入的javascript代碼會(huì)有不同的全局對(duì)象,它可以看做一個(gè)單獨(dú)的javascript程序。但是,要記住,沒有嚴(yán)格關(guān)于javascript程序范圍的定義。如果外邊和里邊的文檔來(lái)自于同一個(gè)服務(wù)器,那么兩個(gè)文檔中的代碼就可以進(jìn)行交互,并且如果你愿意,就可以把他們當(dāng)做同一個(gè)程序的兩個(gè)相互作用的部分。12.8.iii會(huì)詳細(xì)介紹window對(duì)象以及不同窗體之間的交互。

      javascript程序的執(zhí)行有兩個(gè)階段。在第一個(gè)階段,載入文檔內(nèi)容,并執(zhí)行<script>元素的代碼(包括內(nèi)陸腳本和外部腳本)。腳本通常按照它們?cè)谖臋n中出現(xiàn)的順序執(zhí)行。所有腳本里的代碼都是從上往下,按照它在條件、循環(huán)以及其他控制語(yǔ)句中出現(xiàn)的順序執(zhí)行。

      第二個(gè)階段,當(dāng)文檔載入,所有腳本執(zhí)行完成后,javascript就進(jìn)入第二個(gè)階段。這個(gè)階段是異步的,而且是由事件驅(qū)動(dòng)的。在時(shí)間驅(qū)動(dòng)階段,web瀏覽器調(diào)用處理程序函數(shù)(由第一階段里執(zhí)行的腳本指定的html事件處理程序,或之前調(diào)用的時(shí)間處理程序來(lái)定義),來(lái)響應(yīng)時(shí)間異步事件的發(fā)生。調(diào)用事件處理程序通常是響應(yīng)用戶輸入(如鼠標(biāo)單擊,鍵盤按下)。但是還可以由網(wǎng)絡(luò)活動(dòng)、運(yùn)行時(shí)間、或者javascript代碼中的錯(cuò)誤來(lái)觸發(fā)。15章會(huì)詳細(xì)介紹事件和事件處理程序。本章2.ii節(jié)也會(huì)進(jìn)行更多的討論。注意,嵌入在web頁(yè)面里的javascript:URL也可以當(dāng)做一種事件處理的程序,直到用戶單擊或者提交表單之后才會(huì)有效果。

      事件在驅(qū)動(dòng)階段第一個(gè)發(fā)生的事件是load事件,表示文檔已經(jīng)完全載入,并可以操作。javascript經(jīng)常通過(guò)這個(gè)事件來(lái)觸發(fā)或發(fā)送消息。

      我們會(huì)經(jīng)常看到一些定義函數(shù)的腳本程序,除了定義一個(gè)onload事件處理函數(shù)外不做其它操作,這個(gè)函數(shù)會(huì)在腳本事件驅(qū)動(dòng)階段開始時(shí)被load觸發(fā)。正是這個(gè)onload事件會(huì)對(duì)文檔進(jìn)行操作,并做程序想做的任何事。javascript程序的載入是短暫的,通常持續(xù)1到2秒,在文檔載入完成之后,事件驅(qū)動(dòng)階段就會(huì)一直持續(xù)下去。因?yàn)檫@個(gè)階段是異步和事件驅(qū)動(dòng)的,所以可能有長(zhǎng)時(shí)間處于不活動(dòng)狀態(tài)。沒有javascript被執(zhí)行,被用戶或網(wǎng)絡(luò)事件觸發(fā)的活動(dòng)打斷。本章3.iiii javascript執(zhí)行的兩個(gè)階段。

      核心javascript和客戶端javascript都有一個(gè)單線程執(zhí)行模型。腳本和事件處理程序(無(wú)論如何)在同一個(gè)時(shí)間里只能執(zhí)行一個(gè),并沒有并發(fā)性。這保持了javascript編程的簡(jiǎn)單性。本章3.iii會(huì)做介紹。

      i.同步、異步 或延遲的腳本

      javascript第一次添加到web瀏覽器時(shí),還沒有api可以用來(lái)遍歷和操作文檔的結(jié)構(gòu)內(nèi)容,當(dāng)文檔還在載入時(shí),javascript唯一方法就是快速生成內(nèi)容。它使用document.write()完成上述內(nèi)容,下面就是1996最先進(jìn)的javascript的代碼的樣子:

      function factorials(n){ //用來(lái)計(jì)算階乘的函數(shù)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>");

      當(dāng)腳本把文本傳遞給document.write()時(shí),這個(gè)文本被添加到文檔輸入流中,html解析器會(huì)在當(dāng)前位置創(chuàng)建一個(gè)文本節(jié)點(diǎn),將文本插入到這個(gè)文本節(jié)點(diǎn)后面。并不推薦使用document.write(),但在某些場(chǎng)景下有很重要的用途(13.10.ii節(jié))。當(dāng)HTML解析器遇到<script>元素時(shí),它默認(rèn)必須先執(zhí)行腳本,然后再恢復(fù)文檔的解析和渲染。這對(duì)于內(nèi)聯(lián)腳本沒有什么問(wèn)題,但如果在javascript具有src屬性指定外部屬性指定外部條件,這意味著腳本后面的文檔部分在下載和執(zhí)行腳本之前,都不會(huì)出現(xiàn)在瀏覽器中(所謂的“不出現(xiàn)在瀏覽器中”是指文檔的文本內(nèi)容已經(jīng)載入,但是并未被瀏覽器引擎解析為DOM樹,而DOM樹的生成是受javascript代碼“阻塞”頁(yè)面UI的渲染)。

      腳本的的執(zhí)行只在默認(rèn)的情況下是同步和阻塞的。<script>標(biāo)簽可以有defer和async屬性,這可以改變腳本的執(zhí)行方式。這些都是布爾屬性,沒有值;只需要出現(xiàn)在<script>標(biāo)簽里即可。HTML5說(shuō)這些屬性只在Src屬性聯(lián)合使用時(shí)才能有作用,但有些瀏覽器還支持內(nèi)聯(lián)的腳本。

      <script defer src="1.js"></script><script async src="1.js"></script>

      defer和async屬性都像在告訴瀏覽器鏈接進(jìn)來(lái)的腳本不會(huì)使用document.write(),也不會(huì)生成文檔內(nèi)容,因此瀏覽器可以在下載腳本時(shí)繼續(xù)解析和渲染文檔。defer屬性是使得的瀏覽器延遲腳本的執(zhí)行,直到文本的載入和解析完成,并可以操作。async屬性使得瀏覽器可以盡快的執(zhí)行腳本,而不用在下載腳本時(shí)阻塞文檔解析。如果<script>標(biāo)簽同時(shí)有兩個(gè)屬性,同時(shí)支持兩者的瀏覽器會(huì)遵循async屬性并忽略defer屬性。

      注意,延遲的腳本會(huì)按照它們?cè)谖臋n里的出現(xiàn)順序執(zhí)行。而異步腳本在它們載入后執(zhí)行,這意味著它們可能會(huì)無(wú)序執(zhí)行。

      在不支持async的屬性的瀏覽器里,通過(guò)動(dòng)態(tài)的創(chuàng)建<script>元素并把它插入到文檔中,來(lái)實(shí)現(xiàn)腳本的異步載入和執(zhí)行。下面的例子中l(wèi)oadasync()函數(shù)完成了這個(gè)工作。13會(huì)介紹它使用的技術(shù)。

      /*異步載入并執(zhí)行腳本*///異步載入并執(zhí)行一個(gè)指定url中的腳本function loadasync(url) {var head = document.getElementsByTagName("head")[0]; //找到<head>元素var s = document.createElement("script"); //創(chuàng)建一個(gè)<script>元素s.src = url; //設(shè)置其src屬性head.appendChild(s); //將預(yù)算插入head標(biāo)簽中 }loadasync(11.js);loadasync(12.js);loadasync(13.js);

      注意這個(gè)loadasync()函數(shù)會(huì)動(dòng)態(tài)的載入腳本-腳本載入到文檔中,成為正在執(zhí)行的javascript程序的一部分,既不是通過(guò)web頁(yè)面內(nèi)聯(lián)包含,也不算來(lái)自web頁(yè)面的靜態(tài)引用。

      ii.事件驅(qū)動(dòng)的javascript

      在上面的factorials()函數(shù)展示了javascript程序是同步載入的程序:在頁(yè)面載入時(shí)開始執(zhí)行,生成一些輸出,然后結(jié)束。這種類型的程序在今天已經(jīng)不常見了。反之,我們通過(guò)注冊(cè)時(shí)間處理程序來(lái)寫程序。之后在注冊(cè)的事件發(fā)生時(shí)異步調(diào)用這些函數(shù)。例如,想要為常用操作啟用鍵盤快捷鍵的web應(yīng)用會(huì)為鍵盤事件處理程序。甚至非交互的程序也使用事件。假如想要寫一個(gè)分析文檔結(jié)構(gòu)并自動(dòng)生成文檔內(nèi)容的表格程序。程序不需要用戶輸入事件的事件處理程序,但它還是會(huì)注冊(cè)onload事件處理程序。這樣就知道文檔在什么時(shí)候載入完成并可以生成內(nèi)容表格了。

      事件和事件處理是15章的主題,但是這一節(jié)會(huì)提供一個(gè)快速概述。事件都有名字,比如click、change、load、mouseover、keypress、readystatechange,指示發(fā)生的事件的通用類型。事件還有目標(biāo),它是一個(gè)對(duì)象,并且事件就是在它上面發(fā)生的。當(dāng)我們談?wù)撌录r(shí),必須指定事件的類型(名字)和目標(biāo),比如一個(gè)單擊事件發(fā)生在HTMLbutton對(duì)象上,或者一個(gè)readystatechange事件發(fā)生在XMLHttpRequest對(duì)象上。

      如果想要呈現(xiàn)響應(yīng)一個(gè)事件,寫一個(gè)函數(shù),叫做“事件處理程序”、“事件監(jiān)聽器”、“回調(diào)”。然后注冊(cè)這個(gè)函數(shù),這樣它就會(huì)在事件發(fā)生時(shí)調(diào)用它。正如前面提到的,這可以通過(guò)HTML屬性來(lái)完成,不鼓勵(lì)把javascript程序和HTML內(nèi)容混淆在一起。反之,注冊(cè)事件處理程序最簡(jiǎn)單的方法就是把javascript函數(shù)賦值給目標(biāo)對(duì)象屬性,類似這樣寫代碼:

      window.onload = function(){...};document.getElementById("xx").onclick = function(){...};function handleResponse(){...}request.onreadystatechange = handleResponse;

      注意,按照約定事件處理程序的屬性的名字是以“on”開始,后面跟著事件的名字。還要注意在上面的人和代碼里沒有函數(shù)調(diào)用:只是把函數(shù)本身賦值給這些屬性。

      瀏覽器會(huì)在這些事件發(fā)生時(shí)調(diào)用,用事件進(jìn)行異步編程經(jīng)常會(huì)涉及到嵌套函數(shù),也經(jīng)常要在函數(shù)的函數(shù)里定義函數(shù)。

      對(duì)于大部分瀏覽器事件來(lái)說(shuō),會(huì)把一個(gè)對(duì)象傳遞給事件處理程序作為參數(shù),那個(gè)對(duì)象的屬性提供了事件的詳細(xì)信息。比如傳遞給單擊事件的對(duì)象,會(huì)有一個(gè)屬性說(shuō)明那個(gè)按鈕被單擊。(在IE里,這些信息存儲(chǔ)在全局event對(duì)象里,而不是傳遞給處理程序的函數(shù)。)事件處理程序的返回值有時(shí)用指定函數(shù)是否處理了事件。以及阻止瀏覽器執(zhí)行它默認(rèn)會(huì)進(jìn)行的各種操作。

      有些事件的目標(biāo)是文檔元素,它們經(jīng)常往上傳遞給文檔樹,這個(gè)過(guò)程叫“冒泡”。例如,如果用戶在<button>元素上單擊鼠標(biāo),單擊事件就會(huì)在按鈕上觸發(fā)。如果注冊(cè)在按鈕上的函數(shù)沒有處理(并且冒泡停止)該事件。事件冒泡到按鈕嵌套的容器元素。這樣,任何注冊(cè)在元素上的單擊事件都會(huì)調(diào)用。

      如果需要為一個(gè)事件注冊(cè)多個(gè)事件處理程序函數(shù),或者如果想要寫一個(gè)可以安全注冊(cè)事件處理程序的代碼模塊,就算另一個(gè)模塊已經(jīng)為相同的事件注冊(cè)了一個(gè)處理程序,也需要用到另一種事件處理程序注冊(cè)技術(shù)。大部分可以成為事件目標(biāo)對(duì)象都有一個(gè)叫做addEventListaner()方法,允許注冊(cè)多個(gè)監(jiān)聽器:

      window.addEventListener("load",function(){...},false);request.addEventListener("readystatechange",function(){...},false);

      注意,這個(gè)函數(shù)的第一個(gè)參數(shù)是事件的名稱。雖然addEventListener()已經(jīng)標(biāo)準(zhǔn)化超過(guò)了10年,而微軟目前只在IE9里實(shí)現(xiàn)了它。在IE8之前的瀏覽器中,必須使用一個(gè)相似的方法,叫做attachEvent():

      window.attachEvent("onload",function(){...});

      在第15章會(huì)看到更多關(guān)于addEventListener()和attachEvent()內(nèi)容。

      客戶端javascript還使用異步通知類型,這些類型往往不是事件。如果設(shè)置window對(duì)象的onerror屬性為一個(gè)函數(shù),會(huì)發(fā)生(參加12.6節(jié))javascript錯(cuò)誤(或者其它未捕獲的異常)時(shí)調(diào)用函數(shù)。還有setTimeout()和setInterval()函數(shù)(這些是window對(duì)象方法,因此是客戶端javascript的全局函數(shù))會(huì)在指定的一段時(shí)間之后出發(fā)指定函數(shù)的調(diào)用。傳遞給setTimeout()的函數(shù)和真實(shí)時(shí)間處理程序的注冊(cè)不同,它們通常叫做“回調(diào)邏輯”而不是“處理程序”,但它們和時(shí)間處理程序一樣,也是異步的。參加12.1獲得更多關(guān)于setTimeout()和setInterval()函數(shù)的信息。

      下面的例子演示了setTimeout()、addEventlistenter()和attachEvent()、定義一個(gè)onload()函數(shù)注冊(cè)在文檔載入完成時(shí)執(zhí)行的函數(shù)。

      /*當(dāng)文檔載入時(shí)調(diào)用一個(gè)函數(shù)*///注冊(cè)函數(shù)f,當(dāng)文檔載入時(shí)執(zhí)行這個(gè)函數(shù)f//如果文檔已經(jīng)載入完成,盡快以異步的方式執(zhí)行它function onLoad(f) {if (onLoad.loaded) //如果文檔已經(jīng)載入完成window.setTimeout(f, 0); //將f放入異步對(duì)了,并盡快執(zhí)行它else if (window.addEventListener) //注冊(cè)事件的標(biāo)準(zhǔn)方法window.addEventListener("load", f, false);else if (window.attachEvent)window.attachEvent("onload", f);}//給onLoad設(shè)置一個(gè)標(biāo)志,用來(lái)指定文檔是否已經(jīng)載入完成onLoad.loaded = false;//注冊(cè)一個(gè)函數(shù),當(dāng)文檔載入完成時(shí)使用這個(gè)標(biāo)志onLoad(function() {onLoad.loaded = true;});

      iii.客戶端javascript線程模型

      javascript語(yǔ)言核心并不包含任何線程機(jī)制,并且客戶端javascript傳統(tǒng)上也沒有定義任何線程機(jī)制。html5定義了一種作為后臺(tái)線程的"webworker",但是客戶端javascript還是像嚴(yán)格的單線程一樣工作。

      單線程執(zhí)行是為了讓編程更加簡(jiǎn)單。編寫代碼時(shí)可以確保兩個(gè)事件處理程序不會(huì)同一時(shí)刻運(yùn)行。操作文檔內(nèi)容時(shí)不必?fù)?dān)心有其它線程試圖修改文檔。并且永遠(yuǎn)不需要擔(dān)心javascript編寫時(shí)的鎖死,死鎖和竟態(tài)條件。

      單線程執(zhí)行意味這瀏覽器必須在腳本和事件語(yǔ)句程序執(zhí)行時(shí)候停止響應(yīng)用戶輸入。這為javascript程序員帶來(lái)了負(fù)擔(dān)。這意味這javascript腳本和事件處理程序不能運(yùn)行太長(zhǎng)事件。如果一個(gè)腳本執(zhí)行計(jì)算密集的任務(wù),它將會(huì)給文檔載入帶來(lái)延遲。如果事件程序執(zhí)行計(jì)算密集任務(wù),瀏覽器可能變得無(wú)法響應(yīng),可能導(dǎo)致用戶認(rèn)為瀏覽器奔潰了。

      如果程序執(zhí)行的不太多計(jì)算導(dǎo)致明顯的延遲,在文檔沒有完全載入前,可以告知用戶正在運(yùn)行計(jì)算并且瀏覽器沒有掛起。如果有可能可以將其分解為離散子任務(wù)。可以使用setTimeout()和setInterval()在后臺(tái)運(yùn)行子任務(wù)。

      HTML5定義了一種并發(fā)控制方式,“web worker”,它是一個(gè)用來(lái)執(zhí)行計(jì)算密集任務(wù)而不凍結(jié)用戶界面的后臺(tái)線程。運(yùn)行在web worker線程里的代碼不能訪問(wèn)文檔里的內(nèi)容,不能和主線程或其它worker共享狀態(tài),只可以和主線程和其它worker通過(guò)異步事件進(jìn)行通信,所以主線程不能檢測(cè)并非行,而且web worker不能修改javascript程序基礎(chǔ)單線程執(zhí)行模型。20章4節(jié)會(huì)有更多相關(guān)內(nèi)容。

      ?iiii.客戶端javascript時(shí)間線。

      我們已經(jīng)看到j(luò)avascript程序從腳本執(zhí)行階段開始,然后切換到事件處理階段。本節(jié)會(huì)詳細(xì)地解釋javascript程序執(zhí)行的時(shí)間線

    1. web瀏覽器創(chuàng)建Document對(duì)象,并且開始解析web頁(yè)面,解析HTML元素和它們的文本內(nèi)容后添加Element對(duì)象和Text節(jié)點(diǎn)到文檔中,在這個(gè)階段document.readystate的屬性值是"loading".
    2. 當(dāng)HTML解析器遇到async和defer屬性的<script>元素時(shí),它把這些元素添加到文檔中,然后執(zhí)行行內(nèi)或者外部腳本。這些腳本會(huì)同步執(zhí)行,并且在腳本下載(如果需要)和執(zhí)行時(shí)解析器會(huì)暫停。這樣腳本就可以用document.write()來(lái)把文本插入到數(shù)據(jù)流中。解析器恢復(fù)時(shí)這些文本就會(huì)成為文檔的一部分。同步腳本繼承簡(jiǎn)單定義函數(shù)和注冊(cè)后面使用的注冊(cè)事件處理程序,但它們可以遍歷和操作文檔樹,因?yàn)樗麄儓?zhí)行時(shí)已經(jīng)存在了。這樣,同步腳本可以看到它自己的<script>元素和它們之前的文檔內(nèi)容。
    3. 當(dāng)解析器遇到async屬性的<script>元素時(shí),它開始下載腳本文本,并繼續(xù)解析文檔。腳本會(huì)在它下載完成后盡快執(zhí)行,但是解析器沒有停下來(lái)等它下載。異步腳本禁止document.write()方法,它們可以看到自己的<script>元素和它之前的所有文檔元素,并且可能或直接不放我其它文檔內(nèi)容。
    4. 當(dāng)文檔解析完成,document.readyState屬性變成“interactive”。
    5. 所有defer屬性腳本,會(huì)按照文檔里的出現(xiàn)順序執(zhí)行。異步腳本可能也是會(huì)在這個(gè)時(shí)間執(zhí)行,延遲腳本能訪問(wèn)完整的文檔樹,禁止使用document.write()方法。
    6. 瀏覽器在Document對(duì)象上觸發(fā)DOMContentLoaded事件。這標(biāo)志著程序執(zhí)行從同步腳本階段轉(zhuǎn)到了異步事件驅(qū)動(dòng)階段。但要注意,這時(shí)可能還有異步腳本沒有執(zhí)行完成。
    7. 這時(shí),文檔已經(jīng)完全吉祥完成,但是瀏覽器可能還等待其它內(nèi)容的載入,如圖片。當(dāng)所有的內(nèi)容完成載入時(shí),document.readyState屬性變成為"Complete"。瀏覽器從window對(duì)象開始觸發(fā)load事件
    8. 從此刻起,會(huì)調(diào)用異步事件,以異步響應(yīng)用戶輸入時(shí)間、網(wǎng)絡(luò)事件、計(jì)時(shí)器過(guò)期等
    9. 這是一條理想的時(shí)間線(網(wǎng)友自己理解版本)

      1、創(chuàng)建document對(duì)象,開始解析web頁(yè)面。創(chuàng)建HTMLHtmlElement對(duì)象,添加到document中。 創(chuàng)建HTMLHeadElement添加到HTMLHtmlElement中等等,總之遇到不同的標(biāo)簽創(chuàng)建不同的element、node等等,這個(gè)階段document.readyState = 'loading'2、遇到link外部css,創(chuàng)建線程加載,并繼續(xù)解析文檔。3、遇到script外部js,并且沒有設(shè)置async、defer,瀏覽器創(chuàng)建線程加載,并阻塞,等待js加載完成并執(zhí)行該腳本,然后繼續(xù)解析文檔。4、遇到script外部js,并且設(shè)置有async、defter,瀏覽器創(chuàng)建線程加載,并繼續(xù)解析文檔。 對(duì)于async屬性的腳本,腳本加載完成后立即執(zhí)行。 可以采用document.createElement('script')的方式動(dòng)態(tài)插入script元素來(lái)模擬async屬性,實(shí)現(xiàn)腳本異步加載和執(zhí)行。5、遇到img等,瀏覽器創(chuàng)建線程加載,并繼續(xù)解析文檔。6、當(dāng)文檔解析完成,document.readyState = 'interactive'7、文檔解析完成后,所有設(shè)置有defer的腳本會(huì)按照順序執(zhí)行。(注意與async的不同)8、document對(duì)象觸發(fā)DOMContentLoaded事件,這也標(biāo)志著程序執(zhí)行從同步腳本執(zhí)行階段,轉(zhuǎn)化為事件驅(qū)動(dòng)階段。9、當(dāng)所有async的腳本加載完成并執(zhí)行后、img等加載完成后,document.readyState = 'complete',window對(duì)象觸發(fā)load事件。10、從此,以異步響應(yīng)方式處理用戶輸入、網(wǎng)絡(luò)事件等。注:document的每一次readyState屬性變化,都會(huì)觸發(fā)readystatechange事件。

      但是所有的瀏覽器都沒有支持它的全部細(xì)節(jié)。所有的瀏覽器普遍支持load事件,都會(huì)觸發(fā)它。它是決定文檔完全載入并可操作的最通用技術(shù)。

      DOMcontentLoaded事件在load事件之前觸發(fā),當(dāng)前所有的瀏覽器都支持這個(gè)事件,除了IE之外,document.readyState屬性已經(jīng)被大部分瀏覽器實(shí)現(xiàn)。但是這個(gè)屬性在瀏覽器之間還存在差別。async屬性還不通用,使用上文中的loadasync()函數(shù)動(dòng)態(tài)載入腳本的能力能讓程序的執(zhí)行腳本載入階段和事件驅(qū)動(dòng)之間界限更模糊。

      這條時(shí)間線并沒有指定什么時(shí)候文檔開始對(duì)用戶可見或什么時(shí)候web瀏覽器必須開始響應(yīng)用戶輸入事件。這些都是實(shí)現(xiàn)細(xì)節(jié)。對(duì)于很長(zhǎng)的文檔或非常慢的網(wǎng)絡(luò)連接。web瀏覽器理論上會(huì)先渲染一部分文檔。并且在腳本執(zhí)行之前,就能允許用戶和頁(yè)面產(chǎn)生一些交互。這種情況下,用戶輸入事件可能在程序執(zhí)行的事件驅(qū)動(dòng)開始之前觸發(fā)。

      4.兼容性和互用性

      web瀏覽器是web應(yīng)用的操作系統(tǒng),但是web是一個(gè)存在各種差異性的環(huán)境,web文檔和應(yīng)用在不同的操作系統(tǒng)(windows、Mac OS、Linux、iPhone OS、Abdroid)不同開發(fā)商(microsoft、Mozilla、Apple、Google、Opera)的不同時(shí)代的瀏覽器(從預(yù)覽版到類似IE6這種十多年之前的瀏覽器)上查看和運(yùn)行。能夠?qū)懗鲆粋€(gè)健壯的javascript程序并能正確地運(yùn)行在這么多類型的平臺(tái)上,的確是一種挑戰(zhàn)。

      客戶端javascript兼容性和交互性的問(wèn)題可以歸納為以下三個(gè)類:

      演化:

      web平臺(tái)一直在演變和發(fā)展當(dāng)中。一個(gè)標(biāo)準(zhǔn)規(guī)范會(huì)倡導(dǎo)一個(gè)新的特性或API。一個(gè)新的特性看起來(lái)有用,瀏覽器開發(fā)商實(shí)現(xiàn)它,開發(fā)者開始使用這個(gè)特性。有一種情況是新的特性以及被添加到web中,新瀏覽器支持它但是老瀏覽器不支持。web開發(fā)者必先在使用老舊瀏覽器的大量用戶和使用新式瀏覽器的少量用戶之間做出權(quán)衡。

      未實(shí)現(xiàn):

      舉例說(shuō)明:IE8不支持<canvas>元素,雖然其他瀏覽器已經(jīng)實(shí)現(xiàn)它了。一個(gè)更糟糕的例子是,Microsoft決定不實(shí)現(xiàn)DOM Level2 Event規(guī)范(它定義了addEventListener()和相關(guān)方法。。)這個(gè)規(guī)范在12年前就已經(jīng)標(biāo)準(zhǔn)化了,其他瀏覽器廠商已經(jīng)支持了很久了
      bug:
      每個(gè)瀏覽器都有bug,并且沒有按照規(guī)范準(zhǔn)確地實(shí)現(xiàn)所有客戶端javascriptAPI,必須研究已有瀏覽器中的各種bug
      幸運(yùn)的是,javascript語(yǔ)言本身是被所有瀏覽器廠商實(shí)現(xiàn)的。它不是兼容性問(wèn)題的源頭。在老式的瀏覽器ECMAScript3和新式的ECMAScript5之間轉(zhuǎn)換會(huì)導(dǎo)致兼容性問(wèn)題,因?yàn)橐恍g覽器會(huì)支持嚴(yán)格模式而其他的不支持。瀏覽器廠商對(duì)ECMAScript5的實(shí)現(xiàn)是基本相互通用的。

      首先,要解決javascript的兼容性的問(wèn)題是要了解問(wèn)題的根源是什么。web瀏覽器版本更迭更快。我們可以常去這些網(wǎng)站查詢信息:

      MOzilla開發(fā)者中心
      https://developer.mozilla.org

      microsoft 開發(fā)者中心
      https://msdn.microsoft.com/zh-cn/

      apple開發(fā)者中心 safari
      https://developer.apple.com/safari/tools/

      Google Doctype
      致力于幫助Web開發(fā)人員,目前尚處于Beta階段,其中已經(jīng)包含多篇由Google頂級(jí)開發(fā)人員撰寫的關(guān)于網(wǎng)絡(luò)安全、網(wǎng)頁(yè)性能、JavaScript DOM處理、CSS技巧等方面的內(nèi)容,可以作為Web開發(fā)者的參考資料庫(kù)

      http://code.google.com/doctype/

      http://a.deveria.com/caniuse/
      這個(gè)“何時(shí)可用...”站點(diǎn)跟蹤重要web特性實(shí)現(xiàn)的狀態(tài),允許根據(jù)各種標(biāo)準(zhǔn)進(jìn)行過(guò)濾,并在某個(gè)特性只剩下少量已部署的瀏覽器不支持時(shí)推薦使用。

      http://quirksmode.org/dom/

      根據(jù)w3c標(biāo)準(zhǔn)列出各種瀏覽器的DOM兼容性表格

      當(dāng)然,意識(shí)到瀏覽器之間的兼容性問(wèn)題只是第一步。接下來(lái),你需要解決這些不兼容性。一種策略是限制之間使用你選擇支持的所有瀏覽器普遍支持的特性(或者很容易模擬出的特性)。之前提出的“何時(shí)可用...”這個(gè)網(wǎng)站就是圍繞這個(gè)策略的。它列出了ie6淘汰之后才能用的新特性。

      下面介紹一個(gè)消極對(duì)付客戶端不兼容性問(wèn)題的策略。

      i.處理兼容性問(wèn)題的類庫(kù)

      處理不兼容問(wèn)題其中一種最簡(jiǎn)單的方法就是使用類庫(kù)。比如考慮圖像的<canvas>元素(19章主題)、IE(8)是唯一不支持這個(gè)特性的當(dāng)前瀏覽器。在開源的"explerer canvas"項(xiàng)目上有一個(gè)類庫(kù),引入一個(gè)javascript文件叫excanvas.js,然后IE就會(huì)看起來(lái)像支持<canvas>元素一樣。這個(gè)兼容類庫(kù)是一個(gè)很純粹的例子。

      在開發(fā)過(guò)程中,可能會(huì)對(duì)某個(gè)特性編寫類似的庫(kù)。ECMAScript5數(shù)組方法(7.9節(jié)),比如forEach(),map()和reduce(),可以在ECMAScript3中完美模擬,并且通過(guò)把合適的類庫(kù)添加到頁(yè)面中,可以把這些強(qiáng)大有用 的方法當(dāng)做所有瀏覽器平臺(tái)基線的部分。

      但是有時(shí)候,不可能完全地(或有效地)在一個(gè)不支持某個(gè)特性的瀏覽器上實(shí)現(xiàn)一個(gè)特性,就像一件提到的,IE是唯一沒有實(shí)現(xiàn)標(biāo)準(zhǔn)事件處理API的瀏覽器,包括注冊(cè)事件處理程序addEventListener()方法。iE的attachEvent()不像addEventListener()一樣強(qiáng)大,并且在IE提供的繼承上透明地實(shí)現(xiàn)整個(gè)標(biāo)準(zhǔn)并非可行。反之,開發(fā)者要有一個(gè)折中的處理方法,通常叫addEvent,它可以用attachEvent()不像addEventListener()來(lái)方便實(shí)現(xiàn)綁定事件功能。然后,它們?cè)谒械拇a里用addEvent()來(lái)代替addEventListener()和attachEvent()。

      在實(shí)際開發(fā)工作中,今天不少web開發(fā)者在它們的頁(yè)面上使用看客戶端javascript框架。比如jQuery(17章)。使這些框架必不可少的一個(gè)重要功能是:它們定義了新的客戶端API并兼容所有瀏覽器。例如在jQuery中,事件處理程序的注冊(cè)是通過(guò)叫bind()方法完成的,如果你基于jQuery做web開發(fā),就永遠(yuǎn)不需要考慮addEventListener()和attachEvent()之間不兼容的問(wèn)題。

      ii.分級(jí)瀏覽器支持

      分級(jí)瀏覽器支持(graded browser support)是由yahoo!率先提出的一種測(cè)試技術(shù)。分級(jí)瀏覽器中的A級(jí)要通過(guò)所需網(wǎng)頁(yè)完全可用,C級(jí)瀏覽器只需在HTML完整的情況下可用即可,而不需要javascript和css都正常工作,C級(jí)瀏覽器都稱作X級(jí)瀏覽器,這部分是全新或者罕見的瀏覽器。我們默認(rèn)這些瀏覽器網(wǎng)頁(yè)是完全可用的。但官方不會(huì)對(duì)X級(jí)瀏覽器的功能提供完整的支持和測(cè)試。(11年第四季度統(tǒng)計(jì),yahoo!已經(jīng)不再將瀏覽器劃分為A級(jí)和C級(jí)。而是統(tǒng)一給出測(cè)試基準(zhǔn)。根據(jù)這次更新 ,可以明顯感覺到測(cè)試基準(zhǔn)向移動(dòng)端傾斜)

      ?

      iii.功能測(cè)試

      功能測(cè)試(capability testing)是解決不兼容性問(wèn)題的一種強(qiáng)大的計(jì)算。如果你想試用某個(gè)功能,但又不清楚這個(gè)功能是否在所有的瀏覽器中都有比較好的兼容性,則需要在腳本中添加相應(yīng)的代碼來(lái)檢測(cè)是否在瀏覽器中支持該功能。如果期望使用的功能還沒有被當(dāng)前的平臺(tái)所支持,要么不該在平臺(tái)中使用它,要么提供可在平臺(tái)上運(yùn)行的代碼。
      你將會(huì)在后面的各章節(jié)中一次又一次地看到功能體驗(yàn)測(cè)試。例如在第15章,有如下面所示的代碼:

      if (element.addEventListener) { //在使用這個(gè)w3c之前首先檢測(cè)它是否可用element.addEventListener("keydown",handler, false);element.addEventListener("keypress",handler, false);} else if (element.attachEvent) { //在使用該ie方法之前element.attachEvent("onkeydown", handler);element.attachEvent("onkeypress", handler);} else { //否則選擇普遍支持的技術(shù)element.onkeydown = element.onkeypress = handler;}

      關(guān)于功能測(cè)試最重要的是,它并不涉及瀏覽器開發(fā)商和瀏覽器版本號(hào),代碼在當(dāng)前瀏覽器集合中有效,在瀏覽器后續(xù)的版本中也同樣有效,而不管后續(xù)的瀏覽器是否實(shí)現(xiàn)了這些功能集合。但要注意的是:這種方法需要測(cè)試某個(gè)屬性或方法是否在瀏覽器中已經(jīng)定義了,出發(fā)該屬性或方法完全可用,如果Microsoft要定義一個(gè)addEventListener()方法,但Microsoft只是實(shí)現(xiàn)了一部分W3c規(guī)范,在調(diào)用addEventListener()之前這將會(huì)給使用特性的代碼帶來(lái)很多麻煩。

      iiii.怪異模式和標(biāo)準(zhǔn)模式

      Microsoft在發(fā)布IE6的時(shí)候,增加了IE5里沒有的很多css標(biāo)準(zhǔn)特性。但為了確保為了web內(nèi)容的向后兼容性,它定義了兩種不同的渲染摩絲。在“標(biāo)準(zhǔn)模式”或“css兼容模式”中,瀏覽器要遵循css標(biāo)準(zhǔn),在“怪異模式”中,瀏覽器表現(xiàn)的和IE4和IE5中的怪異非標(biāo)準(zhǔn)模式一樣,渲染模式的選擇依賴于html文件頂部的DOCTYPE聲明,在IE6中打開沒有DOCtype的頁(yè)面,會(huì)按照標(biāo)準(zhǔn)模式進(jìn)行渲染。定義了html5 <!DOCTYPE HTML>的頁(yè)面在所有現(xiàn)代瀏覽器都會(huì)按照標(biāo)準(zhǔn)模式渲染。

      怪異模式和標(biāo)準(zhǔn)模式之間的差別經(jīng)歷了很長(zhǎng)的發(fā)展歷程,現(xiàn)在新版的ie都支持標(biāo)準(zhǔn)模式。其它主流的瀏覽器都支持標(biāo)準(zhǔn)模式。這兩種模式都被html5規(guī)范所認(rèn)可。怪異模式和標(biāo)準(zhǔn)模式之前的差異對(duì)于html和css開發(fā)者影響最大。但客戶端javascript代碼則需要知道文檔是以哪種模式進(jìn)行渲染的。要進(jìn)行這種渲染模式的特性檢測(cè),通常檢測(cè)document.compatMode屬性。如果其值為"CSS1Compat",則說(shuō)明瀏覽器告知在標(biāo)準(zhǔn)模式;如果其值為"BackCompat"(或undefined,說(shuō)明屬性不存在),說(shuō)明瀏覽器工作在怪異模式。所有現(xiàn)代的瀏覽器都實(shí)現(xiàn)了compatMode屬性,并且HTML5規(guī)范對(duì)它進(jìn)行了標(biāo)準(zhǔn)化。

      iiiii.瀏覽器測(cè)試

      功能測(cè)試費(fèi)用適用于檢測(cè)大小功能領(lǐng)域的支持,比如可以使用這種方法來(lái)確定瀏覽器是否支持w3c事件處理模式還是IE事件處理模型。另外,有時(shí)候可能需要在某種瀏覽器中解決個(gè)別BUG或難題,但缺沒有太好的方法來(lái)檢測(cè)bug的存在性。

      在客戶端javascript中檢測(cè)瀏覽器的類型和版本的方法就是使用Navigator對(duì)象,我們將在12章學(xué)習(xí)它。在早期,客戶端嗅探就是一種常見的客戶端編程技術(shù),現(xiàn)在的兼容性基本已經(jīng)穩(wěn)定。需要注意的是,客戶端嗅探可以在服務(wù)器端完成,web服務(wù)器根據(jù)User-Agent頭部可以選擇地返回特定的javascript代碼給客戶端。

      5.可訪問(wèn)性

      web是發(fā)布信息的理想工具,而javascript程序可以增強(qiáng)對(duì)信息的訪問(wèn)。然而,javascript程序員必須小心,因?yàn)槌绦騿T寫代碼太過(guò)隨意,以至于那行有視覺障礙或肢體困難的用戶沒辦法正確地獲取信息。

      盲人用戶使用一種叫做屏幕閱讀器的“輔助性技術(shù)”將書面的文字轉(zhuǎn)換為語(yǔ)言詞匯。有些屏幕閱讀器是識(shí)別javascript的,而并一些只能在禁用javascript時(shí)才會(huì)工作得更好。javascript是的角色應(yīng)當(dāng)是增加信息的表現(xiàn)里,而不是負(fù)責(zé)信息的呈現(xiàn)。javascript可訪問(wèn)性的一條重要元素則是,在禁用javascript解釋器的瀏覽器中也能正常使用(或至少某種形式能正常使用)。

      可訪問(wèn)性的另一個(gè)重要原則是,對(duì)于只使用鍵盤但不能(或者選擇不用)鼠標(biāo)的用戶來(lái)說(shuō),如果編寫的javascript代碼依賴特定的鼠標(biāo)事件。這就會(huì)給那行不使用鼠標(biāo)的用戶排除在外。web瀏覽器允許使用鍵盤來(lái)遍歷和激活一個(gè)web頁(yè)面中的UI元素。并且javascript代碼也應(yīng)該允許這樣做。正如15章所介紹,javascript支持獨(dú)立設(shè)備的事件:onfocus和onchange.以及依賴于設(shè)備的事件(onmouseover和onmousedown).為了考慮到可訪問(wèn)性,應(yīng)該盡早可能地支持獨(dú)立設(shè)備的事件。

      創(chuàng)建可訪問(wèn)的web頁(yè)面并非雞毛蒜皮的小事情。關(guān)心可訪問(wèn)性的web應(yīng)用開發(fā)應(yīng)該閱讀這里的文檔http://www.w3.org/WAI/intro/aria。

      6.安全性

      web瀏覽器包含javascript解釋器,也就是說(shuō),一旦載入web頁(yè)面,就可以讓任意的javascript代碼在計(jì)算機(jī)里執(zhí)行。很明顯,這里存在著安全隱患。瀏覽器廠商也在不斷權(quán)衡下面這兩個(gè)之前的博弈:

      • 定義強(qiáng)大的客戶端API,啟用強(qiáng)大的WEB應(yīng)用。
      • 阻止惡意代碼讀取或修改數(shù)據(jù)、盜取隱私、詐騙或浪費(fèi)時(shí)間。

      就像在其它領(lǐng)域中一樣,javascript也在盤根錯(cuò)節(jié)的安全漏洞和補(bǔ)丁之前不斷的發(fā)展變化。在web早期,瀏覽器添加了類似能夠打開、移動(dòng)、調(diào)整窗口大小、已經(jīng)編輯瀏覽器狀態(tài)欄的功能。由于廣告和詐騙的濫用,瀏覽器作者不得不限制和禁用這些API,今天在標(biāo)準(zhǔn)化的 html5中,瀏覽器廠商會(huì)小心(并且開放和合作性地)掂量某個(gè)長(zhǎng)期存在的安全限制,并且在(希望)不引入新的安全漏洞的基礎(chǔ)上給客戶端javascript添加少量的功能。

      下面幾節(jié)會(huì)介紹javascript的安全限制和安全問(wèn)題,這些問(wèn)題是每個(gè)web開發(fā)者都需要意識(shí)到的。

      i.javascript不能做什么
      web瀏覽器征對(duì)惡意代碼的第一條防線就是他們不支持某些功能。例如,客戶端javascript沒有權(quán)限來(lái)寫入或刪除客戶計(jì)算機(jī)上的任意文件或列出任意目錄。這意味著javascript不能刪除數(shù)據(jù)或植入病毒。(20.6.iiiii介紹javascript如何實(shí)現(xiàn)安全隱私文件系統(tǒng),以及如何讀取和寫入文件。)

      類似的客戶端javascript沒有任何通用的網(wǎng)絡(luò)能力。 ?客戶端javascript程序可以對(duì)HTTP協(xié)議編程(參見16章);并且html5有一個(gè)附屬標(biāo)準(zhǔn)膠webSockes,定義一個(gè)類套接字API,用于和指定的服務(wù)器通信。但是這些API都不允許對(duì)于范圍更廣的網(wǎng)絡(luò)進(jìn)行直接訪問(wèn)。通用的Iternet客戶端和服務(wù)器不能同時(shí)使用客戶端javascript來(lái)寫(這里的提示很重要,我們不能基于瀏覽器寫出一個(gè)“服務(wù)器”,網(wǎng)絡(luò)中的瀏覽器和瀏覽器之間無(wú)法直接通信。)

      瀏覽器征對(duì)惡意代碼的第二條防線就是在自己支持某些功能上添加限制,以下是一些功能限制:

      • javascript程序可以打開一個(gè)新的瀏覽器窗口,但是為了防止廣告商濫用彈出窗口,很多瀏覽器限制了這一功能,只有為了響應(yīng)鼠標(biāo)單擊這樣用戶觸發(fā)的時(shí)候才彈出,才能使用它。
      • javascript程序可以關(guān)閉自己打開的瀏覽器窗口,但是不允許不經(jīng)過(guò)用戶允許就關(guān)閉其他窗口。
      • HTML fileupload元素的value屬性是只讀的。如果可以設(shè)置這個(gè)屬性,腳本就能設(shè)置它為任意期望的文件名,從而導(dǎo)致表單上傳指定文件。(比如密碼文件)內(nèi)容到服務(wù)器。
      • 腳本不能從不同的服務(wù)器(嚴(yán)格來(lái)說(shuō),這些服務(wù)器來(lái)自于不同的域,端口或協(xié)議,更詳細(xì)請(qǐng)參照本章6.ii)載入文檔的內(nèi)容,除非這個(gè)就是包含腳本的文檔。類似地,一個(gè)腳本不能來(lái)自不同的服務(wù)器的文檔上注冊(cè)事件監(jiān)聽。這就防止了腳本竊取其它頁(yè)面的用戶輸入(例如組成一個(gè)密碼項(xiàng)的鍵盤單擊過(guò)程),這一項(xiàng)限制叫同源策略,下一節(jié)將詳細(xì)介紹它。

      注意這里并未列出所有客戶端javascript的限制項(xiàng),不同瀏覽器有不同安全策略。并可能實(shí)現(xiàn)的API限制。部分瀏覽器還可能讓用戶偏好決定強(qiáng)弱的限制。

      ii.同源策略

      同源策略是對(duì)javascript代碼能夠操作那些WEB內(nèi)容的一條完整的安全限制 。當(dāng)web頁(yè)面使用多個(gè)<iframe>元素或者打開其它瀏覽器窗口的時(shí)候,這一策略通常就會(huì)發(fā)揮作用。在這種情況下,同源策略賦值管理窗口或窗體中的javascript代碼以及和其它窗口或幀的交互。具體來(lái)說(shuō),腳本只能讀取和所屬文檔來(lái)源相同的窗口和文檔的屬性(參見12章8節(jié)了解如何使用javascript操控多個(gè)窗口和窗體)。

      文檔的來(lái)源包含協(xié)議、主機(jī),以及載入文檔的URL端口。從不同的web服務(wù)器載入的文檔具有不同的來(lái)源。使用http:協(xié)議載入的文檔和使用https:載入的文檔具有不同的來(lái)源。即使他們來(lái)自同一個(gè)服務(wù)器。

      腳本本身的來(lái)源和同源策略并不相關(guān),相關(guān)的是腳本所嵌入的文檔的來(lái)源,理解這一點(diǎn)很重要。例如,一個(gè)來(lái)自于主機(jī)A的腳本被包含到宿主B的一個(gè)web頁(yè)面中,這個(gè)腳本的來(lái)源是主機(jī)B,并且可以完整的訪問(wèn)包含它的文檔的內(nèi)容。如果腳本打開一個(gè)新窗口并載入來(lái)自B主句的另一個(gè)文檔,腳本對(duì)這個(gè)文檔的內(nèi)容也具有完全訪問(wèn)權(quán)限。但是,如果腳本打開第三個(gè)窗口并載入一個(gè)來(lái)自主機(jī)C的文檔(或者來(lái)自主機(jī)A),這個(gè)同源策略就會(huì)發(fā)揮作用,阻止這個(gè)腳本訪問(wèn)這個(gè)文檔。

      實(shí)際上,同源策略并非應(yīng)用不同源的窗口中所有對(duì)象的所有屬性。不過(guò)它應(yīng)用到了其中大多數(shù)屬性,尤其是對(duì)Document對(duì)象的幾乎所有屬性而言。凡是包含另一個(gè)服務(wù)器中文檔的窗口或窗體,都是同源策略的適用范圍。如果腳本打開一個(gè)窗口,腳本也可以關(guān)閉它。但不能以任何方式查看窗口內(nèi)部。同源策略還應(yīng)用于XMLHttpRequests生成的HTTP請(qǐng)求(16章)。這個(gè)對(duì)象允許客戶端javascript生成任意的HTTP請(qǐng)求到腳本所屬文檔的web服務(wù)器。但是不允許腳本和其他web服務(wù)器通信。

      對(duì)于防止腳本竊取有效的信息來(lái)說(shuō),同源策略是必須的。如果沒有這個(gè)限制。惡意腳本(通過(guò)防火墻載入安全的公司內(nèi)外的瀏覽器)可能會(huì)打開一個(gè)空的窗口,欺騙用戶進(jìn)入并使用這個(gè)窗口在網(wǎng)上瀏覽文件 。惡意腳本能夠讀取窗口的內(nèi)容并將其發(fā)送回自己的服務(wù)器。同源策略防止了這種行為。

      不嚴(yán)格的同源策略

      在 某些情況下,同源策略就顯得稍微嚴(yán)格,本節(jié)會(huì)介紹三種不嚴(yán)格的同源策略

      同源策略給那行使用多個(gè)子域的大站帶來(lái)了一些問(wèn)題,例如來(lái)自a.ahthw.com的文檔里的腳本想要合法的從b.ahthw.com讀取文檔的屬性。為了支持這種類型多域名占占,可以使用Document.domain屬性。在默認(rèn)的情況下,domain屬性存放的是載入文檔的服務(wù)器的主機(jī)名。可以設(shè)置這一屬性為ahthw.com
      如果兩個(gè)窗口(或窗體)包含的腳本把domain設(shè)置成了相同的值,那么這兩個(gè)窗口就不再受同源策略的約束。他們可相互讀取對(duì)象的屬性。例如,從c.ahthw.com和d.ahthw.com載入的文檔的腳本可以把他們的document.domain屬性都設(shè)置為ahthw.com,這一依賴,這些文檔就有了同源性,可以相互讀取屬性。

      不嚴(yán)格同源的第二項(xiàng)技術(shù)已經(jīng)標(biāo)準(zhǔn)化為:跨域資源共享(Cross-Origin Resource Sharing,參見http://www.w3.org/TR/cors/)。使用“Origin”請(qǐng)求頭和新的Access-Control-Allow-Origin響應(yīng)頭來(lái)擴(kuò)展HTTP。它允許服務(wù)器用頭信息顯式地列出源,或使用通配符來(lái)匹配所有的源并允許任何地址請(qǐng)求文件。使用這種新的頭信息來(lái)允許跨域HTTP請(qǐng)求,這樣XMLHttpRequest就不會(huì)被同源策略所限制了。

      另外一種新的技術(shù),跨域文檔消息(cross - document messagin),允許來(lái)自一個(gè)文檔的腳本可以傳遞文本消息到另一個(gè)文檔的腳本,而不管腳本的來(lái)源是否不同。調(diào)用window對(duì)象上的 postMessage()方法,可以異步傳遞消息事件(可用onmessage事件句處理海曙來(lái)處理它)到窗口文檔里。一個(gè)文檔里的腳本還是不能調(diào)用在其他文檔里的方法和讀取屬性。但它們可以用這些消息傳遞技術(shù)來(lái)實(shí)現(xiàn)安全的通信(20章3節(jié)有跟多關(guān)于跨文檔消息api的細(xì)節(jié))。

      iii.跨站腳本

      跨站腳本(cross-site scrpting),或者膠XSS,這個(gè)術(shù)語(yǔ)表示一類安全問(wèn)題,也就是攻擊者想目標(biāo)web站點(diǎn)諸如HTML標(biāo)簽或者腳本。防止XSS攻擊是服務(wù)器端WEB開發(fā)者的一項(xiàng)基本規(guī)則。然而,客戶端javascript程序員也必須意識(shí)到或者能夠預(yù)防跨站腳本。

      如果web頁(yè)面動(dòng)態(tài)產(chǎn)生文檔內(nèi)容,并且這些文檔內(nèi)容是基于用戶提交數(shù)據(jù)的,而并沒有通過(guò)從中移除任何嵌入的html標(biāo)簽來(lái)“消毒”的話,這個(gè)頁(yè)面就很容易遭到跨站腳本的攻擊。

      來(lái)看一個(gè)小例子,考慮如下的web頁(yè)面,它使用javascript通過(guò)用戶名字像用戶說(shuō)問(wèn)好。

      var name =decodeURIComponent(window.location.search.substring(1)) ||"";document.write("hello " + name)

      專門通過(guò)以下地址來(lái)調(diào)用

      www.a.com/good.html?Davide

      這時(shí)候,它會(huì)顯示文本"Hello David"。但是考慮一下,使用下面的腳本調(diào)用會(huì)發(fā)生什么樣的情況。

      www.a.com/good.html?%3Cscript%3Ealert("Davide")%3C/script%3E

      使用這個(gè)URL,腳本會(huì)動(dòng)態(tài)的生成另一個(gè)腳本,(%3C和%3E是一個(gè)尖括號(hào)的編碼)在這個(gè)例子中,注入的腳本只顯示一個(gè)對(duì)話框。但是考慮如下情況

      www.b.com/good.html?name=%3Cscript src=siteB/xxx.js%3E%3Cscript%3E

      之所以叫做跨站腳本估計(jì),就是因?yàn)樗婕岸鄠€(gè)站點(diǎn)。站點(diǎn)B專門構(gòu)造到站點(diǎn)A的鏈接,注入來(lái)自站點(diǎn)B的腳本。腳本xxx.js駐留在惡意站點(diǎn)B中,但現(xiàn)在,它嵌入到站點(diǎn)A中,并且可以對(duì)站點(diǎn)A的內(nèi)容進(jìn)行任何想要的操作。它可能損壞這個(gè)頁(yè)面或者使其不能正常工作(例如下節(jié)介紹的拒絕式服務(wù)攻擊)。者可能對(duì)站點(diǎn)A的用戶帶來(lái)不少壞處。

      更危險(xiǎn)的是,惡意腳本可以讀取站點(diǎn)A存儲(chǔ)的Cookie(可能統(tǒng)計(jì)數(shù)據(jù)或者其它個(gè)人驗(yàn)證信息)然后發(fā)送回站點(diǎn)B。注入的腳本甚至可以誘騙用戶點(diǎn)擊將數(shù)據(jù)發(fā)送回站點(diǎn)B。
      通常,防止XSS估計(jì)的方式是,在使用任何不可信的數(shù)據(jù)來(lái)動(dòng)態(tài)創(chuàng)建文檔內(nèi)容之前,從中移除HTML標(biāo)簽。可以通過(guò)下一行代碼來(lái)移除<script>兩邊的尖括號(hào)。

      name = name.replace(/</g,"&lt;").replace(/>/g,"&gt;");

      上面簡(jiǎn)單代碼把字符串中所有的尖括號(hào)替換成他們對(duì)應(yīng)的HTML實(shí)體,也就是說(shuō)將字符串中任意HTML標(biāo)簽進(jìn)行轉(zhuǎn)義過(guò)濾和刪除處理。IE8定義了一個(gè)更加微妙的toStaticHTML()方法,可以移除<script>標(biāo)簽(和其它潛在的可執(zhí)行內(nèi)容)而不修改不可執(zhí)行的HTML。toStaticHTML()是不標(biāo)準(zhǔn)的,但在javascript核心代碼中自己實(shí)現(xiàn)一個(gè)HTML安全函數(shù)也非常簡(jiǎn)單。

      (引用博客園作者小坦克:的預(yù)防提示)

      1.將重要的cookie標(biāo)記為http only, 這樣的話Javascript 中的document.cookie語(yǔ)句就不能獲取到cookie了.2.只允許用戶輸入我們期望的數(shù)據(jù)。 例如: 年齡的textbox中,只允許用戶4輸入數(shù)字。 而數(shù)字之外的字符都過(guò)濾掉。3.對(duì)數(shù)據(jù)進(jìn)行Html Encode 處理4.過(guò)濾或移除特殊的Html標(biāo)簽, 例如: <script>, <iframe> , &lt; for <, &gt; for >, &quot for5.過(guò)濾JavaScript 事件的標(biāo)簽。例如 "οnclick=", "onfocus" 等等。

      HTML5的內(nèi)容安全策略則更進(jìn)一步,它為<iframe>元素定義了一個(gè)sandbox。在實(shí)現(xiàn)之后,它允許顯示不可信的內(nèi)容,并自動(dòng)禁用腳本。
      跨站腳本使有害的漏洞能夠立足web構(gòu)架中,深入理解這些跨站腳本是值得的。很多在線資源可以參考
      http://cert.org/historical/advisories/CA-2000-02.cfm

      iiii.拒絕服務(wù)攻擊
      這里描述同源策略和其他的安全限制可以很好地預(yù)防惡意代碼毀壞數(shù)據(jù)或者防止侵犯隱私這種問(wèn)題。然而根據(jù)不止一種,拒絕服務(wù)攻擊,這種手法非常暴力。比如alert()對(duì)話框無(wú)限占用瀏覽器,或者使用一個(gè)沒有意義的循環(huán)來(lái)占用cpu等。
      利用window.setInterval()方法占用cpu,并分配很多內(nèi)存來(lái)根據(jù)你的系統(tǒng)。web瀏覽器沒有通用的辦法來(lái)放在這種笨重的手法。但是實(shí)際上沒有人會(huì)訪問(wèn)一個(gè)濫用這種腳本的網(wǎng)站。因此在web上不是一個(gè)常見的問(wèn)題。

      7.客戶端框架。

      一些web開發(fā)者基于客戶端框架或類庫(kù)創(chuàng)建它們的web應(yīng)用非常便捷。從某種意義上來(lái)說(shuō),類庫(kù)也是框架。它們對(duì)web瀏覽器提供的標(biāo)準(zhǔn)和專用的API進(jìn)行了封裝,向上提供更高級(jí)的API。
      使用框架的好處就是可以使用更簡(jiǎn)潔的代碼完成更復(fù)雜的功能,此外,完善的框架也會(huì)幫我們處理很多兼容性、安全性和可訪問(wèn)性的問(wèn)題。

      17章會(huì)介紹jQuery,它是當(dāng)前最流行的框架之一。理解底層的API會(huì)幫助你稱為更優(yōu)秀的web開發(fā)者。雖說(shuō)使用他們后很少使用原生的API。

      除了jQuery外,還有很多優(yōu)秀的javascript框架,其中有些框架非常有名,并且廣泛使用。

      Prototype
      Prototype類庫(kù)和jQuery類似,是專門征對(duì)DOM和AJax實(shí)現(xiàn)的一套工具,此外還問(wèn)語(yǔ)言核心擴(kuò)展了很多實(shí)用的工具,scriptaculous就是類庫(kù)基于Prototype實(shí)現(xiàn)的。

      Dojo
      DOjo是一個(gè)大型的框架,它包括一個(gè)種類繁多的UI組件集合、包括管理系統(tǒng)、數(shù)據(jù)抽象層等

      YUI
      YUI是yahoo使用的一個(gè)著名框架,YUI和Dojo一樣龐大,是一個(gè)無(wú)所不包的客戶端類庫(kù),包含一眼工具、DOM、UI組件等。目前有兩個(gè)不兼容的版本YUI2和YUI3

      Closure
      Closure類庫(kù)是Google應(yīng)用Gmail、Docs和其它web應(yīng)用客戶端類庫(kù)。這個(gè)類庫(kù)是打算和Closure編譯器http://code.google.com/closure/compiler/配合使用的,剃除沒用的類庫(kù)函數(shù)。因?yàn)闆]有用的代碼會(huì)在部署之前都被移除。Closure類庫(kù)設(shè)計(jì)者不需要保持特性集合的緊湊。所以Closure包含一個(gè)龐大的工具集。

      GWT
      GWT,即google web toolkit,是一個(gè)完全不同類型的客戶端框架。它用JAVA定義了web應(yīng)用接口,并提供編譯器,將JAVA程序翻譯成兼容的客戶端Javascript。GWT在一些google產(chǎn)品中使用,但不如它們之間的Closure類庫(kù)用的那么廣泛。

      (本文完,歡迎大家關(guān)注上章節(jié)內(nèi)容:第十章:Javascript子集和擴(kuò)展,下章內(nèi)容:第十二章 window對(duì)象

      ?

      轉(zhuǎn)載于:https://www.cnblogs.com/ahthw/p/4299343.html

      總結(jié)

      以上是生活随笔為你收集整理的第十一章:WEB浏览器中的javascript的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

      如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。