第十一章:WEB瀏覽器中的javascript

客戶端javascript涵蓋在本系列的第二部分第10章,主要講解javascript是如何在web瀏覽器中實現的,這些章節介紹了大量的腳本宿主對象,這些對象能夠表示瀏覽器窗口、文檔樹的內容。這些章節一樣涵蓋重要的web應用所須要的網絡編程API、本地存儲和檢索數據、畫圖等。主要包含內容有如下章節:javascript

web瀏覽器中的javascript / window對象 /  腳本化文檔 /  腳本化css / 事件處理 / 校本化http / jQuery類庫 / 客戶端存儲  /  多媒體和圖形編程 / HTML5APIcss

本書的第一部分介紹了javascript語言核心,第二部分開始轉向web瀏覽器中的javascript討論。迄今爲止,咱們的大部分例子是合法的javascript代碼,帶是沒有特定的上下文,也就是說它們運行在不明的環境總。本章節提供了一個能夠容許javascript上下文。html

在開始討論javascript以前,考慮下web瀏覽器是怎麼呈現頁面的,其靜態頁面稱爲文檔(document),相對於文檔來講,洽談web頁面甘江更像是應用。若是須要的話,這些頁面能夠載入新的新想,所以看起來圖形化,而非文本化,而且它們能夠進行離線操做,以及保存數據倒本地,以便再次訪問進行狀態恢復。此外,還有其它web頁面處於文檔和應用的中間,結合了二者的特性。html5

本章以客戶端javascript概述開始,包括一個簡單的例子,以及對javascript如何在web文檔和web應用中角色討論。概述內容還介紹了那些內容會在後續章節中提到,接下來會詳細解釋javascript代碼在html文檔中如何嵌入,而後介紹兼容性、可訪問性和安全性等問題。java

1.客戶端javascriptnode

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(),能夠註冊一個函數,在給定的一些時間內觸發一個回調web

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

注意上面的代碼並無顯式的使用window 屬性。在客戶端javascript中,window對象 也是全局對象。這意味着window對象處於做用域鏈頂部,它的屬性和方法其實是全局變量和全局函數window對象有一個自身引用的屬性,叫作window。若是須要引用窗口對象自己,引用引用這個屬性可是若是隻想引用全局窗口對象的屬性,一般不須要用到windowchrome

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類型:

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

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" onchange="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. web瀏覽器建立Document對象,而且開始解析web頁面,解析HTML元素和它們的文本內容後添加Element對象和Text節點到文檔中,在這個階段document.readystate的屬性值是"loading".
  2. 當HTML解析器遇到async和defer屬性的<script>元素時,它把這些元素添加到文檔中,而後執行行內或者外部腳本。這些腳本會同步執行,而且在腳本下載(若是須要)和執行時解析器會暫停。這樣腳本就能夠用document.write()來把文本插入到數據流中。解析器恢復時這些文本就會成爲文檔的一部分。同步腳本繼承簡單定義函數和註冊後面使用的註冊事件處理程序,但它們能夠遍歷和操做文檔樹,由於他們執行時已經存在了。這樣,同步腳本能夠看到它本身的<script>元素和它們以前的文檔內容。
  3. 當解析器遇到async屬性的<script>元素時,它開始下載腳本文本,並繼續解析文檔。腳本會在它下載完成後儘快執行,可是解析器沒有停下來等它下載。異步腳本禁止document.write()方法,它們能夠看到本身的<script>元素和它以前的全部文檔元素,而且可能或直接不放我其它文檔內容。
  4. 當文檔解析完成,document.readyState屬性變成「interactive」。
  5. 全部defer屬性腳本,會按照文檔裏的出現順序執行。異步腳本可能也是會在這個時間執行,延遲腳本能訪問完整的文檔樹,禁止使用document.write()方法。
  6. 瀏覽器在Document對象上觸發DOMContentLoaded事件。這標誌着程序執行從同步腳本階段轉到了異步事件驅動階段。但要注意,這時可能還有異步腳本沒有執行完成。
  7. 這時,文檔已經徹底吉祥完成,可是瀏覽器可能還等待其它內容的載入,如圖片。當全部的內容完成載入時,document.readyState屬性變成爲"Complete"。瀏覽器從window對象開始觸發load事件
  8. 今後刻起,會調用異步事件,以異步響應用戶輸入時間、網絡事件、計時器過時等

這是一條理想的時間線(網友本身理解版本

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等,瀏覽器建立線程加載,並繼續解析文檔。

六、當文檔解析完成,document.readyState = 'interactive'7、文檔解析完成後,全部設置有defer的腳本會按照順序執行。(注意與async的不一樣)

8、document對象觸發DOMContentLoaded事件,這也標誌着程序執行從同步腳本執行階段,轉化爲事件驅動階段。

九、當全部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章,有以下面所示的代碼:

            if (element.addEventListener) { //在使用這個w3c以前首先檢測它是否可用
                element.addEventListener("keydown",handler, false);
                element.addEventListener("keypress",handler, false);
            } else if (element.attachEvent) { //在使用該ie方法以前
                element.attachEvent("onkeydown", handler);
                element.attachEvent("onkeypress", handler);
            } else { //不然選擇廣泛支持的技術
                element.onkeydown = element.onkeypress = handler;
            }

關於功能測試最重要的是,它並不涉及瀏覽器開發商和瀏覽器版本號,代碼在當前瀏覽器集合中有效,在瀏覽器後續的版本中也一樣有效,而無論後續的瀏覽器是否實現了這些功能集合。但要注意的是:這種方法須要測試某個屬性或方法是否在瀏覽器中已經定義了,出發該屬性或方法徹底可用,若是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>兩邊的尖括號。

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

上面簡單代碼把字符串中全部的尖括號替換成他們對應的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> ,  &lt; for <, &gt; for >, &quot for
            5.過濾JavaScript 事件的標籤。例如 "onclick=", "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對象

相關文章
相關標籤/搜索