AJAX相關總結

AJAX「Asynchronous Javascript And XML」(異步JavaScript和XML),是指一種建立交互式網頁應用的網頁開發技術。
javascript

  • AJAX = 異步 JavaScript和XML(標準通用標記語言的子集)
  • AJAX 是一種用於建立快速動態網頁的技術
  • 經過在後臺與服務器進行少許數據交換
  • AJAX可使網頁實現異步更新。這意味着能夠在不從新加載整個網頁的狀況下,對網頁的某部分進行更新。
  • 傳統的網頁(不使用 AJAX)若是須要更新內容,必須重載整個網頁頁面。

什麼是AJAX?

AJAX只是一個前端技術,不是一個新的語言。它是利用瀏覽器提供操做HTTP的接口(XMLHttpRequest或者ActiveXObject)來操做HTTP以達到異步的效果。
php

網頁渲染的同步與異步的區別

  • 同步:當你在瀏覽器的地址欄裏輸入百度網址並訪問的時候,瀏覽器都是建立新的tabpage、新的內存塊、加載網頁的所有資源、渲染加載過來的資源。這些那都是從頭開始的,就想上帝創做世界同樣。只要與後臺交互數據,那怕數據只有那麼一丟丟,也得從新創造一次世界,如此反覆。瀏覽器本身控制HTTP操做
  • 異步:不須要從新創造一次世界,用以前已經存在的世界來達到目的。與後臺交互數據不須要從新來渲染頁面.本身來控制HTTP操做。

HTTP介紹

HTTP (HyperText Transfer Protocol) 超文本傳輸協議 在當前web環境中走的流量大部分都是走的HTTP流量,也就是說你在瀏覽器中訪問任何東西,那怕是一張小圖片也是HTTP來給你當搬運工顯示在你面前的。並且AJAX就是基於HTTP來傳輸數據的。因此要想精通AJAX,適當的瞭解並掌握HTTP是十分必要的。css

web客戶端和服務器

web內容都是存儲在web服務器上的,web服務器所使用的是http協議,所以常常會被稱爲http服務器。這些http服務器存儲量因特網中的數據,若是http客服端發出請求的話,它們會提供數據。客戶端向服務器發送http請求,服務器會在http響應中會送所請求的數據。http客服端和http服務器共同構成了萬維網的基本組建。可能你天天都是使用http客服端。最多見的http客戶端就是瀏覽器。web瀏覽器向服務器請求http對象,並將這些對象顯示在你的屏幕上。html

HTTP事務

一個http事務由一條(從客戶端發往服務器的)請求命令和一個(從服務器發回客戶端的)響應結果組成。這種通訊時經過名爲HTTP message的格式化數據塊進行的。
只有當請求和響應都成功時此http事務纔算成功,也就是這條http纔算成功。只有當其中任意一個命令(請求或者響應)失敗,那麼這個http就算失敗。
一個http就是一個http事務,且http事務完成以後此http不可在複用。前端

http報文

http報文是由一行一行的簡單字符串組成的。http報文都是純文本,不是二進制代碼,因此人們能夠很方便地對其進行讀寫。

http報文分爲三部分:java

  • 起始行
    報文的第一行就是起始行,在請求報文中用來講明作些什麼,在響應報文中說明出現了什麼狀況。
  • 首部字段
    起始行後面有零個或多個首部字段。每一個首部字段都包含了一個名字和一個值,首部分爲5種類型:

    通用首部、請求首部、響應首部、實體首部、擴展首部
  • 主體
    報文主體包含了全部類型的數據。請求主體中包含了要發送給web服務器的數據;響應主體中裝載了要返回給客戶端的數據。起始行和首部字段都是結構化的文本形式的,而主體能夠包含任意的二進制數據。固然,主體中也能夠包含文本。

HTTP 方法

http支持幾種不一樣的請求命令,這些命令被稱爲http方法 每條http請求報文都包括一個方法。這個方法會告訴服務器執行什麼動做(獲取一個web頁面、運行一個網關程序、刪除一個文件)。
node

常見來http方法以下web

  • GET 從服務器向客戶端發送命名資源,主要是傳給服務器一些參數來獲取服務器上指定的資源。
  • POST 將客戶端數據發送到一個服務器網關程序。
  • DELETE 從服務器上刪除命名資源
  • HEAD 僅發送命名資源中的http首部
  • PUT 未來自客戶端的數據存儲到一個服務器資源中去
  • TRACE 對報文進行追蹤
  • OPTIONS 決定能夠從服務器上執行哪些方法

GET與POST的區別:
ajax

  • URL長度限制 瀏覽器對URL有大小限制,chrome 8k firefox 7k ie 2k
  • 資源大小限制:get方法限制大小,get是將數據直接拼接在URL後端query部分,而瀏覽器是對URL有長度限制的,因此get有大小限制。post不限制大小。由於post是將數據放到請求的主體裏,而主體是不限制大小的,因此post沒有大小限制。
  • 功能 get主要是用來從服務器拉取數據,而post主要是用來將數據發送到服務器。
  • 安全 get能夠看到發送給服務器的數據,而post不會被看到,由於post把數據放到主體裏了。

HTTP 狀態碼

每條http響應報文返回時都會攜帶一個狀態碼。狀態碼是一個三位數字的代碼,告知客戶端請求是否成功,或者是否須要採起其餘操做。

幾種常見的狀態碼:chrome

  • 200 OK 文檔正確返回
  • 202 put和delete請求成功返回
  • 301 Redirect 永久重定向。一直從其餘地方去獲取資源
  • 302 Redirect 臨時重定向。臨時到其餘地方去獲取資源
  • 303 see other、307 Temporary Redirect 將客服端重定向到一個負載不大的服務器上,用於負載均衡和服務器失聯
  • 404 Not Found 沒法找到這個資源
  • 500 Internal Server Error 服務器錯誤

伴隨着每一個數字狀態碼,http還會發送一條解釋性的緣由短語文本。包含文本短語主要是爲了進行描述,全部的處理過程使用的都是數字碼。

http軟件處理下列狀態碼和緣由短語的方式是同樣的:

  • 200 OK
  • 200 Document attached
  • 200 Success
  • 200 All's cool, dude

MIME Type

因特網上有數千種不一樣的數據類型,http仔細地給每種要經過web傳輸的對象都打上了名爲MIME類型(MIME Type)的數據格式標籤。最初設計MIME(Multipurpose Internet Mail Extension,多用途因特網郵件擴展) 是爲了解決在不一樣的電子郵件系統之間搬移報文時存在的問題。MIME在電子郵件系統中工做得很是好,所以HTTP也採納了它,用它來描述並標記多媒體內容。

web服務器會爲全部的http對象數據附加一個MIME類型。當web瀏覽器從服務器中取回一個對象時,回去查看相關的MIME類型,看看它是否知道應該如何處理這個對象。大多數瀏覽器均可以處理數百種常見的對象類型。

MIME Type就是告訴瀏覽器用什麼方式來處理這個數據。

MIME類型是一種文本標記,表示一種主要的對象類型和一個特定的子類型,中間由一條斜槓來分隔。

  • html格式的文本文檔由text/html類型來標記
  • 普通的ASCII文本文檔由text/plain類型來標記
  • JPEG格式的圖片爲image/jpeg類型
  • GIF格式的圖片爲image/gif類型
  • 表單提交由application/x-www-form-urlencoded類型來標記

MIME類型在HTTP協議中的表現爲Request Header或者Response Header中的Content-Type

URI、URL、URN

URI : 每一個web服務器資源都有一個名字,這樣客戶端就能夠說明他們感興趣的資源是什麼了。服務器資源名被稱爲統一資源標識符(Uniform Resource Identifier,URI) URI就像因特網上的郵政地址同樣,在世界範圍內惟一標識並定位信息資源。

例如 https://www.baidu.com/img/baidu_jgylogo3.gif
這是一個百度服務器上一個圖片資源的URI

URL : 統一資源定位符(URL) 是資源標識符最多見的形式。URL描述了一臺特定服務器上某資源的特定位置。它們能夠明確說明如何從一個精確、固定的位置獲取資源。

例如 :https(http協議)://www.baidu.com(進入百度服務器)/img/baidu_jgylogo3.gif(獲取路徑爲/img/baidujgylogo3.gif的圖片)
大部分的URL都遵循一種標準格式,這種格式分爲三部分。

  • 第一部分 方案(scheme),說明了訪問資源所使用的協議類型。着部分一般就是http協議。
  • 第二部分 服務器位置,給出來服務器的因特網地址(好比,www.baidu.com)。
  • 第三部分 資源路徑,指定了web服務器上的某個資源(好比,/img/baidujgylogo3.gif)。
    如今,幾乎全部的URI都是URL
    大多數的URL方案的URL語法都創建在這個有9部分構成的通用格式上:

    <scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

URN : URI 的第二種形式就是統一資源名稱(URN)。URN是做爲特定內容的惟一名稱使用的,與目前資源地無關。使用這些與位置無關的URN>,就能夠將資源四處搬移。經過URN,還能夠用同一個名字經過多種網絡訪問協議來訪問資源。

好比,不論因特網標準文檔RFC 2141 位於何處(甚至能夠將其複製到多個地方),均可以用下列URN來命名它:

urn:ietf:rdc:2141

URN目前仍然處於試驗階段。

URI包括兩種形式,一種是URL一種是URN。目前大部分會不加區別的使用URI和URL。由於URL是URI的一個子集。

方案

方案其實是規定如何訪問指定資源的主要標識符,它會告訴負責解析URL的應用程序應該使用什麼協議,最多見的就是HTTP方案。
常見的方案格式:

  • http 超文本傳輸協議方案,默認端口爲80
  • https 方案https和方案http是一對,爲一個區別在於使用了網景的SSL,SSL爲http提供了端到端的加密機制。默認端口爲443
  • mailto 指向Email地址。
  • ftp 文件傳輸協議,能夠用來從ftp服務器上傳下載文件
  • file 訪問本地文件
  • telnet 用於交互式訪問業務

瀏覽器兼容性

在IE7如下版本的IE系列瀏覽器中,要應用AJAX必須使用ActiveXObject(這個對象是一個微軟推廣和支持在Internet Explorer中,不在Windows應用商店的應用程序。) 方法。在標準瀏覽器(chrome、firefox、opera、safari、ie7+)當中則使用XMLHttpRequest對象。

如何發起AJAX?

在低版本IE(7-)中使用ActiveXObject構造AJAX對象時須要傳入一個String類型的參數Microsoft.XMLHTTP,也可使用Msxml3.XMLHTTPMsxml2.XMLHTTP。由於一開始是Microsoft.XMLHTTP 以後變成Msxml2.XMLHTTP及更新版的Msxml3.XMLHTTP

// code for IE6, IE5
 var xmlhttp1 = new ActiveXObject("Microsoft.XMLHTTP");
 var xmlhttp2 = new ActiveXObject("Msxml2.XMLHTTP");
 var xmlhttp3 = new ActiveXObject("Msxml3.XMLHTTP");

在標準瀏覽器中則使用XMLHttpRequest對象

// code for IE8+, Firefox, Chrome, Opera, Safari
var xmlhttp = new XMLHttpRequest();

爲了在項目中能夠在任何瀏覽器中使用AJAX因此咱們必須作一個判斷,若是瀏覽器爲低版本IE就使用ActiveXObject對象不然使用XMLHttpRequest對象。代碼以下:

var XHR = function () {
    var xmlhttp;
    if (window.XMLHttpRequest) {
        // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp = new XMLHttpRequest();
    }
    else {
        // code for IE6, IE5
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    return xmlhttp;
  };
var xmlObj=XHR();
console.log(xmlObj);

這樣的話,咱們就能夠獲得一個在任何瀏覽器都能發起AJAX的方法。可是這樣左右一個壞處,就是每次獲取AJAX對象時都會判斷一次,這樣作很費時費力。因此,咱們利用惰性函數的概念來實現一個只須要第一次判斷,後面都不須要判斷的方法。

var XHR = function () {
 //將瀏覽器支持的AJAX對象放入一個function中,而且根據固定的順序放到一個隊列裏。
        for (var AJAXObj = [function () {
            return new XMLHttpRequest
        }, function () {
            return new ActiveXObject("Msxml2.XMLHTTP")
        }, function () {
            return new ActiveXObject("Msxml3.XMLHTTP")
        }, function () {
            return new ActiveXObject("Microsoft.XMLHTTP")
        }], val = null, index = 0; index < AJAXObj.length; index++) {
        //此方法的核心,若是當前瀏覽器支持此對象就用val保存起來,用保存當前最適合ajax對象的function替換XHR方法,而且結束該循環。這樣第二次執行XHR方法時就不須要循環,直接就能獲得當前瀏覽器最適ajax對象。若是都不支持就拋出自定義引用錯誤。
            try {
                val = AJAXObj[index]()
            } catch (b) {
                continue
            }
            //假設當前瀏覽器爲標準瀏覽器,此處執行完畢以後console.log(XHR);
            //結果爲:function () {
            //  return new XMLHttpRequest
            //};XHR成功替換。
            XHR=AJAXObj[index];
            break
        }
        if (!val) {
            throw new ReferenceError("XMLHttpRequest is not supported")
        }
        return val;
    };
  var xmlObj=XHR();
  console.log(xmlObj);

本方法的核心就是利用 惰性函數 。這纔是正點。第一次計算獲得的值,供內部函數調用,而後用這個內部函數重置外部函數(由於同名),之後就不用計算了,也不用判斷分支條件。這時函數就至關於一個被賦值的變量。

接下來咱們依次介紹XMLHttpRequestActiveXObject如何使用。

使用XMLHttpRequest

XMLHttpRequest 是一個 JavaScript 對象,它最初由微軟設計,隨後被 Mozilla,Apple, 和 Google採納. 現在,該對象已經被 W3C組織標準化. 經過它,你能夠很容易的取回一個URL上的資源數據. 儘管名字裏有XML, 但XMLHttpRequest 能夠取回全部類型的數據資源,並不侷限於XML. 並且除了HTTP ,它還支持file 和 ftp 協議.

在瀏覽器中建立並使用一個 XMLHttpRequest 實例, 可使用以下語句:

var req = new XMLHttpRequest();
//do something...

XMLHttpRequest 讓發送一個HTTP請求變得很是容易。你只須要簡單的建立一個請求對象實例,打開一個URL,而後發送這個請求。當傳輸完畢後,結果的HTTP狀態以及返回的響應內容也能夠從請求對象中獲取。本頁把這個強大的JavaScript對象的一些經常使用的甚至略有晦澀的使用案例進行了一下概述。

XMLHttpRequest對象方法概述:

  • 返回值 方法(參數)
  • void abort();停止操做
  • DOMString getAllResponseHeaders();獲得全部響應頭
  • DOMString? getResponseHeader(DOMString header);獲得指定響應頭
  • void open(DOMString method, DOMString url, optional boolean async, optional DOMString? user, optional DOMString? + password);開啓XMLHttpRequest對象
  • void overrideMimeType(DOMString mime);重寫MIME類型
  • void send();發送請求,此方法有六種重載
  • void send(ArrayBuffer data);發送二進制流
  • void send(Blob data);發送二進制塊
  • void send(Document data);發送文檔
  • void send(DOMString? data);發送字符串
  • void send(FormData data);發送格式化表單數據
  • void setRequestHeader(DOMString header, DOMString value);設置請求頭

XMLHttpRequest對象屬性概述:

  • 屬性名 格式類型 說明
  • onreadystatechange Function? 一個JavaScript函數對象,當readyState屬性改變時會調用它。回調函數會在用戶接口線程中調用。(警告: 不能在本地代碼中使用. 也不該該在同步模式的請求中使用.)
  • readyState unsigned short 請求的五種狀態: 0 UNSENT (未打開) open()方法還未被調用、 1 OPENED (未發送) send()方法還未被調用、2 HEADERS_RECEIVED (已獲取響應頭) send()方法已經被調用, 響應頭和響應狀態已經返回、 3 LOADING (正在下載響應體) 響應體下載中; responseText中已經獲取了部分數據、 4 DONE (請求完成) 整個請求過程已經完畢.
  • response varies 響應實體的類型由 responseType 來指定, 能夠是 ArrayBuffer, Blob, Document, JavaScript 對象 (即 "json"), 或者是字符串。若是請求未完成或失敗,則該值爲 null。
  • responseText DOMString 這次請求的響應爲文本,或是當請求未成功或還未發送時爲 null。只讀。
  • responseType XMLHttpRequestResponseType 設置該值可以改變響應類型。就是告訴服務器你指望的響應格式: "" (空字符串) 字符串(默認值)、 "ArrayBufferView" ArrayBufferView、 "blob" Blob、 "document" Document、 "json" JavaScript Object、 "text" 字符串。
  • responseXML Document? 本次請求的響應是一個 Document 對象,若是是如下狀況則值爲 null:請求未成功,請求未發送,或響應沒法被解析成 XML 或 HTML。當響應爲text/xml 流時會被解析。當 responseType 設置爲"document",而且請求爲異步的,則響應會被當作 text/html 流來解析。只讀.(注意: 若是服務器不支持 text/xml Content-Type 頭,你可使用 overrideMimeType() 強制 XMLHttpRequest 將響應解析爲 XML。)
  • status unsigned short 該請求的響應狀態碼 (例如, 狀態碼200 表示一個成功的請求).只讀.
  • statusText DOMString 該請求的響應狀態信息,包含一個狀態碼和緣由短語 (例如 "200 OK"). 只讀.
  • upload XMLHttpRequestUpload 能夠在 upload 上添加一個事件監聽來跟蹤上傳過程。
  • withCredentials boolean 代表在進行跨站(cross-site)的訪問控制(Access-Control)請求時,是否使用認證信息(例如cookie或受權的header)。 默認爲 false。注意: 這不會影響同站(same-site)請求.
XMLHttpRequest和本地文件

網頁中可使用相對URL的能力一般意味着咱們能使用本地文件系統來開發和測試HTML,並避免對web服務器進行沒必要要的部署。而後當使用XMLHttpRqeust進行Ajax編程時,這一般是不可行的。XMLHttpRequest用於HTTP和HTTPS協議一塊兒工做。理論上,它可以同像FTP這樣的其餘協議一塊兒工做,但好比像請求方法和響應狀態碼等部分API是HTTP特有的。若是從本地文件中加載網頁,那麼該網頁中的腳本將沒法經過相對URL使用XMLHttpRequest,由於這些URL將相對於file://URL而不是http://URL。而同源策略一般會阻止使用絕對http://URL。結果是當使用XMLHttpRequest時,爲了測試它們一般必須把文件上傳到wen服務器(或運行一個本地服務器)

方法
abort()
req.abort();

若是請求已經被髮送,則馬上停止請求(cancel).abort()方法在全部的XMLHttpRequest版本和XHR2中可用,調用abort()方法在這個對象上觸發abort事件。能夠經過XMLHttpRequest對象的onabort屬性是否存在來判斷。

//if成立的話abort()存在,不然不存在
if('onabort' in req){
   req.abort();
}
getAllResponseHeaders()
var allHeaders = req.getAllResponseHeaders();

返回全部響應頭信息(響應頭名和值), 若是響應頭還沒接收,則返回null. (注意: 對於多部分請求,這將返回頭從請求的當前一部分,而不是從原來的通道上。)

getResponseHeader()
var dateHeader = req.getAllResponseHeaders("Date");

返回指定的響應頭的值, 若是響應頭還沒被接受,或該響應頭不存在,則返回null.

open()

注意: 調用此方法必須已經發出主動請求(open()或openRequest()已經被調用)是至關於調用abort()。

req.open(http Method,URL,isAsync,userName,password);
//參數
//http Method 請求所使用的HTTP方法; "POST" 或者 "GET". 若是下個參數是非HTTP(S)的URL,則忽略該參數.
//URL 該請求所要訪問的URL
//isAsync 一個可選的布爾參數,默認爲真,指示是否異步執行操做。若是該值爲false時,send()方法不返回直到收到響應。若是爲true,完成交易的通知使用事件偵聽器提供。這必須是真實的,若是多部分屬性爲true,或將引起異常。
//userName 可選的用戶名,以用於身份驗證的目的;默認狀況下,這是一個空字符串。
//password 可選的密碼用於認證的目的;默認狀況下,這是一個空字符串。

初始化一個請求. 該方法用於JavaScript代碼中;若是是本地代碼, 使用 openRequest()方法代替.

若是傳入的http method不區分大小寫與 CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT, TRACE, 或者 TRACK匹配上, 從範圍0x61(ASCII a)每一個字節0x7A(ASCII a)減去0x20。把小寫轉換成大寫(若是它不匹配任何上述狀況,它是經過傳遞字面上,包括在最後的請求。)

若是http method 不區分大小寫匹配到 CONNECT, TRACE, 或者 TRACK 這三個方法, 拋出 "SecurityError" 異常,由於安全風險已被明確禁止。舊瀏覽器並不支持全部這些方法,但至少HEAD獲得普遍支持

overrideMimeType()
req.overrideMimeType("text/plain");
//參數必須爲MIME Type格式

重寫由服務器返回的MIME類型。這能夠用於一下狀況。假如你將下載XML文件,而你計劃把它當成純文本對待。可使用overrideMimeType()讓XMLHttpRequest知道它不須要把文件解析爲XML文檔:

//不要把響應做爲XML文檔處理
req.overrideMimeType('text/plain; charset=utf-8');

這個方法必須send()以前調用。

onload
req.onload=function(){
 console.log(this.responseText,this.responseType,this.response);
}

當XMLHttpRequest對象加載完成時(readyState爲4)觸發。且只與readyState有關,與status和statustext無關。因此當註冊onload的方法執行時不必定爲成功的狀態。只是也僅僅是這個條http事務完成而已。不註冊此方法則onload默認爲null。

onreadystatechange
req.onreadystatechange=function(){
//判斷ajax成功,此寫法有兼容性。
 if(this.readyState==this.DONE&&this.statusText=="OK"){
  console.log(this.response,'成功');
 }
//判斷ajax成功還有另一種寫法
 if(this.readyState==4&&this.status==200){
  console.log(this.responseText,'成功');
 }
}

每當readyState的值改變時就會出發該方法。不註冊此方法則onreadystatechange默認爲null。

send()

注意: 全部相關的事件綁定必須在調用send()方法以前進行.

req.send(undefined||null||ArrayBufferView||Blob||XML||String||FormData);
//此方法有7種參數重載

發送請求. 若是該請求是異步模式(默認),該方法會馬上返回. 相反,若是請求是同步模式,則直到請求的響應徹底接受之後,該方法纔會返回.

GET請求絕對沒有主體,因此應該傳遞null或者省略這個參數。POST請求一般擁有主體,同時它應該匹配使用setRequestHeader()指定的Content-Type頭。

若是數據是一個Document,它在發送以前被序列化。當發送文件時,Firefox以前的版本3的版本老是使用UTF-8編碼發送請求; Firefox 3的正常使用發送,若是沒有指定編碼由body.xml編碼,或者UTF-8指定的編碼文件。

setRequestHeader()
req.setRequestHeader("header","value");
//設置制定的請求頭,此方法必須在send()執行以前執行。
//header 將要被賦值的請求頭名稱.
//value 給指定的請求頭賦的值.

給指定的HTTP請求頭賦值.在這以前,你必須確認已經調用 open() 方法打開了一個url.

若是對相同的頭調用setRequestHeader()屢次,新值不會取代以前指定的值,相反,HTTP請求將包含這個頭的多個副本或者個頭將指定多個值。例如:

req.setRequestHeader("Accepts",'text/html');
req.setRequestHeader("Accepts",'text/css');
//那個請求頭中的Accepts的值爲 「text/html,text/css」

你不能制定Content-LengthDateRefererUser-Agent頭,XMLHttpRequest將自動添加這些頭而防止僞造他們。相似地,XMLHttpRequest對象自動處理cookie、鏈接時間、字符集和編碼判斷,因此你沒法向setRequestHeader()傳遞這些頭:

  • Accept-Charset
  • Content-Transfer-Encoding
  • Date
  • Connection
  • Expect
  • Content-Length
  • Host
  • Cookie
  • Keep-Alive
  • User-Agent
  • Cookie2
  • Referer

你能爲請求指定Authorization頭,但一般不須要這麼作。若是請求一個受密碼保護的URL,把用戶名和密碼做爲第四個和第五個參數傳遞給open),則XMLHttpRequest將設置合適的頭。

順序問題

HTTP請求的各部分有指定順序:請求方法和URL首先到達,而後是請求頭,最後是請求主題。MXLHttpRequest實現一般直到調用send()方法纔開始啓動網絡。單XMLHttpRequest API的設計彷佛使每一個方法都將寫入網絡流。這意味着調用XMLHttpRequest方法的順序必須匹配HTTP請求的架構。例如,setRequestHeader()方法的調用必須在調用send()以前但在調用open()以後,不然它將拋出異常。

示例代碼:

//一些簡單的代碼作一些與數據經過網絡獲取的XML文檔
function processData(data) {
  // taking care of data
}

function handler() {
  if(this.readyState == this.DONE) {
    if(this.status == 200 &&
       this.responseXML != null &&
       this.responseXML.getElementById('test').textContent) {
      // success!
      processData(this.responseXML.getElementById('test').textContent);
      return;
    }
    // something went wrong
    processData(null);
  }
}

var client = new XMLHttpRequest();
client.onreadystatechange = handler;
client.open("GET", "unicorn.xml");
client.send();

//若是你只是想記錄一個消息服務器
function log(message) {
  var client = new XMLHttpRequest();
  client.open("POST", "/log");
  client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
  client.send(message);
}

//或者,若是您要檢查服務器上的文檔的狀態
function fetchStatus(address) {
  var client = new XMLHttpRequest();
  client.onreadystatechange = function() {
    // in case of network errors this might not give reliable results
    if(this.readyState == this.DONE)
      returnStatus(this.status);
  }
  client.open("HEAD", address);
  client.send();
}

在低版本IE瀏覽器中使用ActiveXObject時須要注意的地方

使用ActiveXObject與XMLHttpRequest對象大致相同。不過仍是有許多不一樣的地方。

不一樣點:

  • ActiveXObject對象中沒有timeout屬性,沒有ontimeout方法。
  • ActiveXObject對象中不支持statustext屬性。
  • ActiveXObject對象中沒有DONE、OPEN、UNSENT、HEADERS_RECEIVED、DONE 這些屬性。
  • ActiveXObject對象中沒有onload方法。
  • ActiveXObject對象中send()不支持ArrayBuffer|Blob|Formdata 等類型重載。
  • ActiveXObject對象中沒有withCredentials屬性。

    因爲ActiveXObject對象只在IE五、IE6中使用,因此不少功能都沒有。因此使用時需注意。

ActiveXObject對象用法:

var http;
  if(window.XMLHttpRequest){
   http = new XMLHttpRequest();
  } else if (window.ActiveXObject){
   http = new ActiveXObject("Microsoft.XMLHTTP");
   if (!http){
    http = new ActiveXObject("Msxml2.XMLHTTP");
   }
  }
 function handleStateResponse() {
  if (this.readyState == 4) {
   if(this.status == 200) {
    var results = this.responseText;
    console.log(results);
   } else {
    alert ( "Not able to retrieve description" );
   }
  }
 }
 http.open();
 http.onreadystatechange=handleStateResponse;
 http.send();

AJAX 示例

上面的基礎知識和使用處理ajax的對象須要注意的一些地方咱們都都已經提到了,如今咱們開始寫ajax示例,以便更好的鞏固ajax知識。

這個示例是參照jQuery的ajax函數使用方法來編寫的,而且可使用鏈式調用以加強體驗。

最終代碼以下:

//編寫一個$http對象,這個對象下有get()、post()、getScript()、ajax()這些方法。
//每一個方法都返回一個可供鏈式註冊的對象。
//ajax()方法提供多個固定的參數以達到高配置性。
(function (global, undefined) {
    //若是全局已經存在$http對象的話就直接返回,不執行任何代碼。
    if (global.$http) {
        return
    }
    var http = global.$http = {};
    //判斷類型
    function isType(str) {
        return function (obj) {
            return Object.prototype.toString.call(obj) == '[object ' + str + ']';
        }
    }

    var isObject = isType("Object");
    var isFunction = isType("Function");
    var isNumber = isType("Number");
    var isString = isType("String");
    var isBoolean = isType('Boolean');
    var isArray = isType('Array');
    //檢測參數中有沒有`?`
    var hasSearch = function (url) {
        return /^.+\?[^?]*$/g.test(url)
    };
    //循環幫助函數
    var each=(function(){
         if ([].forEach)
             return function(arr,func){
                 [].forEach.call(arr, func);
             }
         else
             return function(arr,func){
                for (var i = 0; i < arr.length; i++) {
                   func.call(arr, arr[i], i, arr);
                }
             }
    })();

    //獲取ajax對象,
    var getXHR = function () {
        for (var list = [function () {
            return new XMLHttpRequest
        }, function () {
            return new ActiveXObject("Microsoft.XMLHTTP")
        }, function () {
            return new ActiveXObject("Msxml2.XMLHTTP")
        }, function () {
            return new ActiveXObject("Msxml3.XMLHTTP")
        }], temp = null, index = 0; index < list.length; index++) {
            try {
                temp = list[index]()
            } catch (ex) {
                continue
            }
            getXHR = list[index];
            break;
        }
        if (!temp) {
            throw new ReferenceError("browser is not supported")
        }
        return temp;
    };


    http.ajax = function (options) {
        // 若是options不是一個對象那麼此方法後續將不執行。
        if (!isObject(options)) return;

        //默認參數對象
        var defaultOptions = {
            //添加到請求頭`Accepts`中,用來講明這個Ajax接收什麼MIME Type的數據。Array類型
            accepts: undefined,
            //是否爲異步,默認爲true。Boolean類型
            async: true,
            //在執行send()方法以前調用的函數。Function類型
            beforeSend: undefined,
            //是否緩存。Boolean類型
            cache: false,
            //無論成功或失敗都會執行的函數。Function類型
            complete: function () {},
            //添加到請求頭`Content-Type`中,標識此http是什麼MiME Type,默認爲`application/x-www-form-urlencoded; charset=UTF-8`。String類型
            contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
            //宿主對象。Object類型
            context: undefined,
            //發送給服務器的數據。Object或者String類型
            data: undefined,
            //服務器返回的數據格式,能夠爲`xml|bolb|arraybuffer|html|script|json|text`。String類型
            dataType: 'text',
            //過濾服務器返回數據。Function類型
            dataFilter: undefined,
            //執行失敗、parseError或者timeout執行的函數。Function類型
            error: function (xhr, xhr.status, text) {},
            //自定義頭信息。Object類型
            headers: {},
            //重寫服務器返回MIME Type。String類型
            mimeType: '',
            //URL認證密碼。String類型
            password: undefined,
            //註冊對應http狀態碼時執行的函數。Object類型
            statusCode: {},
            //Ajax成功時執行的函數。Function類型
            success: function (response) {},
            //超時毫秒值,此值必須大於500毫秒,不然不生效。Number類型
            timeout: undefined,
            //請求的http方法,能夠爲`get|post|head|put|delete`。String類型
            type: '',
            //請求的URL,此URL的方案不能夠爲`file`。String類型
            url: '',
            //URL認證帳號。String類型
            username: undefined,
            //在send()執行前,經過此函數操做xhr對象。Function類型
            setXhrFields: function(xhr){}
        },
         //臨時變量
         tempVal;

        //覆蓋默認參數對象
        for (tempVal in defaultOptions) {
            if (options.hasOwnProperty(tempVal) && options[tempVal])
                defaultOptions[tempVal] = options[tempVal];
        }

        var xhr = getXHR(), _promise = new __promise();

        if (!/^(get|post|head|put|delete)$/img.test(defaultOptions.type)) {
            throw new ReferenceError('not supported this http method');
        }
        if (!/^(xml|bolb|arraybuffer|html|script|json|text)/img.test(defaultOptions.dataType)) {
            throw new TypeError('not supported ' + defaultOptions.dataType + ' data type');
        }
        if (!isString(defaultOptions.url)) {
            throw new TypeError('url must be a string')
        }
        //是否使用緩存,不使用則在URL後添加一個隨機數
        if (defaultOptions.cache) {
            defaultOptions.url += (hasSearch(defaultOptions.url) ? '_=' : '?_=') + Math.random() * (1 << 24) | 0;
        }
        //將發送給服務器的數據從Object類型轉爲String類型
        if (defaultOptions.data) {
            if (isObject(defaultOptions.data)) {
                var arr = [];
                for (tempVal in defaultOptions.data) {
                    if (defaultOptions.data.hasOwnProperty(tempVal)) {
                        // 此處參見下文的表單操做
                        arr.push(encodeURIComponent(tempVal) + "=" + encodeURIComponent(defaultOptions.data[tempVal]));
                    }
                }
                defaultOptions.data = arr.join('&');
            }
            //當請求的http方法爲get、head、delete中一個時直接拼接到URL後而且刪除data。參見send()方法解釋。
            if (/^(get|head|delete)$/img.test(defaultOptions.type)) {
                defaultOptions.url += (hasSearch(defaultOptions.url) ? '' : '?') + defaultOptions.data;
                delete  defaultOptions.data;
            }
        }

        defaultOptions.setXhrFields(xhr);

        xhr.open(defaultOptions.type, defaultOptions.url, defaultOptions.async, defaultOptions.username, defaultOptions.password);
        if(isArray(defaultOptions.accepts)){
           each(defaultOptions.accepts,function(x){
              xhr.setRequestHeader('Accepts',x);
           })
        }
        for (tempVal in defaultOptions.headers) {
            if (defaultOptions.headers.hasOwnProperty(tempVal))
                xhr.setRequestHeader(tempVal, defaultOptions.headers[tempVal]);
        }
        xhr.setRequestHeader("Content-Type", defaultOptions.contentType);

        if (defaultOptions.context) {
            //綁定宿主對象
            defaultOptions.complete = defaultOptions.complete.bind(defaultOptions.context);
            defaultOptions.success = defaultOptions.success.bind(defaultOptions.context);
            defaultOptions.error = defaultOptions.error.bind(defaultOptions.context);
        }

        //將success、error、complete方法包裹起來
        var _success = function (text) {
            defaultOptions.success(text);
            _promise._done(text);
            defaultOptions.complete(xhr);
            _promise._always();
        }, _error = function (text) {
            defaultOptions.error(xhr, xhr.status, text);
            _promise._fail(xhr, xhr.status, text);
            defaultOptions.complete(xhr);
            _promise._always();
        };


        //檢測xhr對象是否有responseType,若是有則將dataType賦給它
        ('responseType' in xhr)&& (xhr.responseType = defaultOptions.dataType);
        xhr.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (defaultOptions.mimeType) {
                    //重寫MIME Type
                    xhr.overrideMimeType(defaultOptions.mimeType);
                }
                //當http狀態碼爲2xx時執行success,4xx或5xx時執行error
                if (/^2\d{2}$/.test(this.status)) {
                    var returnVal = undefined;
                    if (xhr.responseType) {
                        returnVal = this.response;
                    } else {
                        //數據處理
                        switch (defaultOptions.dataType.toLowerCase()) {
                            case 'xml':
                                returnVal = this.responseXML;
                                break;
                            case 'html':
                                var frag = document.createDocumentFragment();
                                frag.innerHTML = this.responseText;
                                returnVal = frag;
                                break;
                            case 'script':
                                var scr = document.createElement("script");
                                scr.innerHTML = this.responseText;
                                returnVal = scr;
                                break;
                            case 'json':
                                if (global.JSON.parse) {
                                    try {
                                        returnVal = JSON.parse(this.responseText);
                                    } catch (ex) {
                                        _error(ex)
                                    }
                                } else {
                                    returnVal = eval('(' + this.responseText + ')');
                                }
                                break;
                            case 'arraybuffer':
                                throw new ReferenceError('not supported arraybuffer');
                            case 'blob':
                                throw new ReferenceError('not supported blob');
                            default:
                                returnVal = this.responseText;
                        }
                    }
                    _success(defaultOptions.dataFilter(returnVal)||returnVal);
                }
                if (/^(4|5)\d{2}$/.test(this.status)) {
                    _error();
                }
                //根據http狀態碼執行statusCode中對應的函數
                (this.status.toString() in defaultOptions.statusCode) && defaultOptions.statusCode[this.status]();
            }
        };
        if (defaultOptions.beforeSend) {
            defaultOptions.beforeSend(xhr);
        }
        xhr.send(defaultOptions.data);
        xhr.onerror = _error;
        //超時設置,此方法有兼容性。因此執行前先作特性判斷
        if (isNumber(defaultOptions.timeout) && defaultOptions.timeout > 500) {
            if ('timeout' in xhr) {
                xhr.timeout = defaultOptions.timeout;
                xhr.ontimeout = defaultOptions.error;
            } else {
                setTimeout(function () {
                    if (xhr.readyState != 4) {
                        _error();
                    }
                }, defaultOptions.timeout);
            }
        }
        //返回鏈式註冊對象
        return _promise;
    };
    //利用ajax方法生產get、post方法
    each(['get', 'post'], function (x) {
        http[x] = function (url, data, func, datatype) {
            if (arguments.length != 4)
                throw new TypeError('lacking arguments');
            return http.ajax({
                url: url,
                data: data,
                success: func,
                dataType: datatype,
                type: x
            })
        }
    });
    //利用ajax生產getScript方法
    http.getScript = function (url, data, func) {
        return http.ajax({
            url: url,
            type: 'get',
            data: data,
            success: func,
            dataType: 'script'
        })
    };
    //鏈式調用對象
    function __promise() {
        //設置默認函數
        this._done = this._fail = this._always = function () {
        };
    }

    //這三個方法執行完以後都必須將本身返回,不然沒法鏈式註冊
    __promise.prototype.done = function (func) {
        this._done = func;
        return this;
    };
    __promise.prototype.fail = function (func) {
        this._fail = func;
        return this;
    };
    __promise.prototype.always = function (func) {
        this._always = func;
        return this;
    };

    //AMD, CommonJs, then globals
        if (typeof define === 'function' && define.amd) {
            define([], function(){
                return http;
            });
        } else if (typeof exports === 'object') {
            module.exports = http;
        } else {
            global.$http =  global.$http || http;
        }
})(window);
如何使用上面的Ajax庫
window.onload=function(){
            //假設本地有一臺端口爲1111的web服務器,且這個web服務器上有一個名爲ajaxAPI的接口
            //能夠這樣使用
            $http.get('http://localhost:1111/ajaxAPI','arg=1',function(x){
                console.log(toString.call(x),x)
            },'json').done(function(){
                console.log('done')
            }).fail(function(e){
                console.error(e)
            }).always(function(){
                console.info('always');
            });
            //也能夠這樣使用
            $http.ajax({
                type:'get',
                url:'http://localhost:1111/ajaxAPI',
                context:{a:'123'},
                success:function(x){
                    console.log(x,this.a);
                }
            });
            //... 你還能夠根據ajax中提供的參數編寫功能更增強大的函數
        }

表單操做

考慮到HTML表單。當用戶提交表單時,表單中的數據(每一個表單元素的名字和值)編碼到一個字符串中隨請求發送。默認狀況下,HTML表單經過POST方法發送給服務器,而編碼以後的表單數據則用作請求主體。對錶單數據使用的編碼方案相對簡單:對每一個表單元素的名字和值執行偶痛的URL編碼(使用十六進制轉義碼替換特殊字符;編碼利用encodeURIComponent()方法,解碼利用decodeURIComponent()方法),使用等號把編碼後的名字和值分開,並使用&符號分開名/值對。一個簡單表單的編碼以下這樣:

name=pizza&age=18&address=%E5%8C%97%E4%BA%AC

表單數據編碼格式有一個正式的MIME類型

application/x-www=form-urlencoded

當使用post方法提交這順序的表單數據時,必須設置Content-Type請求頭爲這個值。

注意:這種類型的編碼並不須要HTML表單,在本章咱們實際上將不須要直接使用表單。在Ajax應用中,你但願發給服務器的多是個javascript對象。前面展現的數據變成javascript對象的表單編碼形式可能爲:

{
    name:pizza,
    age:18,
    address:'北京'
}

表單編碼在web上如此普遍的使用,同時全部服務器端的編程語言都能很好的支持,因此非表單數據的表單編碼也是容易實現的事情。以下代碼:

function encodeFormData(data){
    if(!data) return '';
    var arr=[];
    for(var name in data){
        if(!data.hasOwnProperty(name)) continue;
        if(typeof data[name] === 'function') continue;
        var value = data[name] + '';
        name=encodeURIComponent(name);
        value=encodeURIComponent(value);
        arr.push(name + '=' + value);
    }
    return arr.join('&');
}

使用已定義的encodeFormData()函數,咱們能容易的將javascript對象轉化爲表單格式的數據。

表單數據一樣能夠經過get請求來提交,既然表單提交的目的是爲了執行只讀查詢,所以get請求比post更合適。get請求歷來沒有主體,因此須要發送給服務器的表單編碼數據負載要做爲URL的查詢(search)部分。

利用ajax模擬表單提交

在應用場景中,模擬表單提交是十分常見的。在HTML中form元素提交以後會從新加載頁面,這個現象的反作用是很大的,須要從新加載整個頁面的數據,因此更多的是經過ajax模擬表單提交數據,這樣的話實現的功能是和html的表單提交一摸同樣可是不須要從新加載頁面。在實現模擬表單提交以前須要先實現一個表單序列化的幫助函數。代碼以下:

http.serialize = function (form) {
        var parts = [], optValue = "";
        each(form.elements, function (ele) {
            switch (ele.type) {
                case 'select-one':
                case 'select-multiple':
                    if (ele.name) {
                        each(ele.options, function (option) {
                            if (option.selected) {
                                 if (option.hasAttribute) {
                                      optValue = (option.hasAttribute("value") ? option.value : option.text);
                                 } else {
                                      optValue = (option.attributes["value"].specified ? option.value : option.text);
                                 }
                                parts.push(encodeURIComponent(ele.name) + "=" + encodeURIComponent(optValue));
                            }
                        })
                    }
                    break;
                case undefined:
                case 'submit':
                case 'reset':
                case 'button':
                case 'file':
                    break;
                case 'radio':
                case 'checkbox':
                    if (!ele.checked) {
                        break;
                    }
                default :
                    if (ele.name) {
                        parts.push(encodeURIComponent(ele.name) + "=" + encodeURIComponent(ele.value));
                    }
            }
        });
        return parts.join('&');
    }

    //給表單註冊onsubmit事件。當提交時就序列化表單數據併發送給服務器
    document.forms[0].onsubmit=function(){
        $http.get('/serialize',$http.serialize(this),function(res){
            console.log(res);
        });
        //阻止默認行爲
        return false;
    };

上面這個serialize()函數首先定義了一個名爲parts的數組,用於保存將要建立的字符串的各個部分。而後,經過for循環迭代每一個表單字段,並將其保存在field變量中。在得到了一個字段的引用以後,使用switch語句檢測其type屬性。序列化過程最麻煩的就是select元素,它多是單選框也多是多選框,值可能有一個選中項,而多選框則可能有零或多個選中項。這裏的代碼適用於這兩種選擇框,至於可選框的數量是由瀏覽器控制的。在找到了一個選中項以後,須要肯定使用什麼值。若是不存在value特性,或者雖然存在該特性,但值爲空字符串,要使用選項的文本代替。爲檢查這個特性,在DOM兼容的瀏覽器中須要使用hasAttribute()方法,而在IE中須要使用特性的specified屬性.

若是表單中包含fieldset元素,則該元素會出如今元素集合中,但沒有type屬性。所以,若是type屬性未定義,則不須要對其進行序列化。一樣,對於各類按鈕以及文件輸入字段也是如此。對於單選按鈕和複選框,要檢查其checked屬性是否被設置爲false,若是是則退出switch語句。若是checked屬性爲ture,則繼續執行default語句,即將當前字段的名稱和值進行編碼,而後添加到parts數組中。函數的最後一步,就是使用join()格式化整個字符串,也就是用和號來分割每個表單字段。

最後,serialize()函數會以查詢字符串的格式輸出序列化以後的字符串。固然,要序列化成其它格式,也不是什麼困難的事。


跨域請求操做

在應用場景中會常常出現不一樣源的數據請求,這種狀況下須要作相應的處理操做。因爲同源策略和瀏覽器兼容等問題的存在,因此處理不一樣來源的請求須要注意的地方有不少。下面會爲你們列出相應的解決方案以及相應的概念普及。

同源策略

同源策略就是規定了javascript能夠操做那些web內容的一個完整的安全限制。

什麼是同源?

同源就是規定多個web資源的url中schemehostnameport必須相同,只要有一項不一樣那麼這個web資源就不是同源的。同時,同源策略就會其相應的做用來限制這個web資源。

同源策略爲何會出現?

對於防止腳本竊取全部內容來講,同源策略是很是有必要的。若是沒有這一個限制,惡意腳本可能會打開一個空頁面,誘導用戶進入並使用這個窗口在內網瀏覽操做文件。這樣的話,惡意腳本就可以讀取窗口內的內容發送到本身的服務器來達到竊取數據的目的。而同源策略就是限制了這種行爲。

思考:web的安全性如何考慮?

跨域HTTP請求

什麼是跨域?

當請求的資源的URL與當前頁面的URL中的schemehostnameport有一個不一樣的時候就算是跨域操做。請參見上面的同源。

由於有同源策略的限制,XMLHttpRequest僅能夠發起操做同域(同源)下的請求。雖然這個限制關閉了安全漏洞可是也阻止了大量合法的適合使用的跨域請求。不過這種狀況下也能夠在頁面中使用imgformiframe等元素中使用跨域URL,最中在瀏覽器中顯示這些數據。可是由於同源策略,瀏覽器不容許操做或者不能良好的顯示跨域文檔內容。
若是此處使用XMLHttpRequest來操做跨域請求,那麼全部的文檔內容都將在responseText屬性中暴露,因此同源策略不容許XMLHttpRequest進行跨域請求。

注意:img元素會把返回的內容強制轉換爲圖片。iframe元素不容許操做跨域數據.

可是須要強調的是script元素並未真正受到同源策略的限制,由於script有可能須要加載不一樣域的javascript資源。須要加載並執行任何來源的腳本。正由於如此,script的靈活性使其成爲在跨域操做中代替XMLHttpRequest的主流Ajax傳輸協議:JSONP

JSONP

script元素能夠做爲一種Ajax傳輸協議,只需設置script元素的src屬性而且插入到DOM中,瀏覽器就會發出一個HTTP請求到src屬性所指向的URL。使用script元素進行Ajax傳輸的一個主要緣由就是由於它不受同源策略的影響。所以能夠發送一個不一樣源的請求。而另一個緣由就是用script元素會自動解碼並執行(瀏覽器會當作javascript來處理)下載的數據。

JSONP帶來的安全性考慮:
爲了使用script元素來進行Ajax傳輸,你必須容許web頁面信任並執行目標服務器返回過來的任何數據。這意味這對於不信任的服務器,不該該採起該技術。在與信任的服務器交互是還要提防攻擊者可能會進入服務器中。因此做爲Ajax數據傳輸的script與可信的服務器交互,是至關危險的事情。

使用這種script元素來進行Ajax數據的傳輸的技術就叫作'JSONP',也就是JSON-padding.這個P(padding)表明的是 填充、補充、前綴。在於服務器返回的數據必須用javascript的方法名加括號包裹住才行。而不是僅僅發送一段JSON格式的數據而已,要是這樣的話瀏覽器只會對返回的數據進行JSON解碼,結果仍是數據,並無作任何相應的處理。

所以在使用JSONP的時候須要注意的是,服務器返回的數據是有固定格式的。例如:

//服務器不能夠返回這樣的數據
["jeams","bond",{NAME:"OBAMA",AGE:56}]
//服務器會返回一個這樣的響應
functionName(["jeams","bond",{NAME:"OBAMA",AGE:56}])

其中的functionName必須是在window下能夠訪問的名稱。這樣的話服務器就不只僅只是返回一段JSON數據而已了,同時還會執行對應的操做。包裹後的響應會成爲這個script元素的內容,它先判斷JSON編碼後的數據(由於是一個javascript表達式),而後把數據傳給functionName函數。此處咱們能夠假設functionName會那這些數據作有用的事情。
可是爲了可行起見,咱們必需要把須要包裹數據的那個javascript方法名告訴服務器,也就是上面例子裏的那個functionName。這樣的話服務器就會知道用什麼來包裹須要返回的數據了。服務器也能夠知道返回的是一個JSONP數據而不是一個普通的JSON數據。例如能夠在請求的URL後面加上?callback=functionName

//實現一個簡單的JSONP請求
//請求的url、包裹方法名稱、回調函數
function JSONP(url,callbackName,callback){
    //爲本次請求建立一個惟一的callback名稱
    var cbnum="cb"+JSONP.count++; //計數器 生成一個惟一的名稱
    var cbname="JSONP."+cbnum; //做爲JSONP方法的一個靜態屬性

    if(url.indexOf("?")==-1){
        url+="?"+callbackName+"="+cbname;
    }else{
        url+="&"+callbackName+"="+cbname;
    }

    JSONP[cbnum]=function(response){
        try{
            callback(response);
        }catch (ex){

        }finally{
            //執行完畢以後就刪掉,由於沒什麼用了
            delete JSONP[cbnum];
            script.parentNode.removeChild(script);
        }
    }
    var script=document.createElement("script");
    script.src=url;
    document.body.appendChild(script);
}
//初始化用於建立惟一名稱的計數器
JSONP.count=0;

//發起JSONP請求。
JSONP("http://suggestion.baidu.com/su?wd=xxx","cb",function(data){
    //將百度返回的數據輸出到控制檯中
    console.log(data)
});

注意:當屢次執行同一個url的JSONP操做時須要注意緩存問題。

可用的jsonp接口:

查詢淘寶商品:

1:http://suggest.taobao.com/sug?code=utf-8&q=商品關鍵字&callback=cb;

快遞查詢:

2:http://www.kuaidi100.com/query?type=quanfengkuaidi&postid=390011492112;
(ps:快遞公司編碼:申通="shentong" EMS="ems" 順豐="shunfeng" 圓通="yuantong" 中通="zhongtong" 韻達="yunda" 每天="tiantian" 匯通="huitongkuaidi" 全峯="quanfengkuaidi" 德邦="debangwuliu" 宅急送="zhaijisong")

天氣查詢:

3:http://php.weather.sina.com.cn/iframe/index/w_cl.php?code=js&day=0&city=&dfc=1&charset=utf-8;

手機號查詢:

4:http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=手機號;

百度搜素:

5:http://suggestion.baidu.com/su?wd=a&cb=xxx;

跨域資源共享

因爲瀏覽器的同源策略,限制了XMLHttpRequest的跨域請求的操做。可是在XHR2中瀏覽器選擇容許發送合適的CORS(cross-origin resource sharing,跨域資源共享)來跨域請求數據。在標準瀏覽器中依舊使用XMLHttpRequest對象,而在IE8-9中則使用XDomainRequest對象來請求跨域資源。

雖然實現CORS不須要作任何事情,可是還有一些安全細節須要瞭解。首先,若是經過XMLHttpRequest的open()方法傳入用戶名和密碼(詳情見open方法),那麼它們毫不會經過跨域請求發送。另外跨域請求也不會包含其餘任何的用戶證書:cookie和HTTP身份認證的令牌(TOKEN)一般不會做爲請求的內容發送
到對方的服務器且對方服務器返回任何數據(cookie以及其餘的一些響應頭)都將被丟棄。若是跨域請求必須須要傳入這幾種用戶證書才能成功,那麼就必須在調用send()方法以前設置XMLHttpRequest的WithCredentials爲true,此屬性默認爲false。也能夠檢索XMLHttpRequest對象有沒有該屬性來判斷是否它支持CORS操做。

withCredentials屬性

默認狀況下,在瀏覽器中使用XMLHttpRequest進行跨源請求不提供憑據(cookie、HTTP認證及客戶端SSL證實等)。經過將XMLHttpRequest的withCredentials屬性設置爲true,能夠指定某個請求應該發送憑據。若是服務器接收帶憑據的請求,會用下面的HTTP頭部來響應。

//服務器端返回此響應頭
Access-Control-Allow-Credentials: true
//XMLHttpRequest的withCredentials設置爲true
xhr.withCredentials=true;

若是發送的是帶憑據的請求,但服務器的相應中沒有包含這個頭部,那麼瀏覽器就不會把相應交給JavaScript(因而,responseText中將是空字符串,status的值爲0,並且會調用onerror()事件處理程序)。另外,服務器還能夠在響應中發送這個HTTP頭部,表示容許源發送帶憑據的請求。

使用XDomainRequest對象時須要注意的地方:

一、XDomainRequest對象沒有onreadystatechange屬性。

二、此對象只有IE8中有。

三、此方法只可以使用http方案和https方案。

四、此對象還有一個特殊的contentType屬性,用來得到響應頭中的Content-Type。

當瀏覽器使用跨域資源共享時,無論是使用XMLHttpRequest仍是XDoaminRequest。服務器都必須在響應頭中設置Access-Control-Allow-Origin

//在java或者C#中
<% Response.AddHeader("Access-Control-Allow-Origin","*") %>
//nodejs中
response.writeHead(200,{"Access-Control-Allow-Origin":"*"})

其中代碼容許任何源請求本服務器,也能夠改爲固定的源。例如:{"Access-Control-Allow-Origin":"http://localhost:63342"} 只容許URL爲http://localhost:63342的請求源請求本服務器。
警告:若是將XMLHttpRequest的withCredentials屬性設置爲true的時候,Access-Control-Allow-Origin這個響應頭不能夠設置爲*

W3C規定的跨域資源共享中服務器能夠返回的頭信息以下:
  1. Access-Control-Allow-Origin

    做用:設置能夠跨域請求此服務器的域名

    使用格式:Access-Control-Allow-Origin = "Access-Control-Allow-Origin" ":" ascii-origin | "*"
  2. Access-Control-Max-Age

    做用:代表在指定多少秒內,不須要再發送預檢驗請求,能夠緩存該結果

    使用格式:Access-Control-Max-Age = "Access-Control-Max-Age" ":" delta-seconds
  3. Access-Control-Allow-Credentials

    做用:容許攜帶 用戶認證憑據(也就是容許客戶端發送的請求攜帶Cookie)

    使用格式:Access-Control-Allow-Credentials: "Access-Control-Allow-Credentials" ":" "true"
  4. Access-Control-Allow-Methods

    做用:代表它容許哪些指定HTTP方法的外域請求

    使用格式:Access-Control-Allow-Methods: "Access-Control-Allow-Methods" ":" #Method
  5. Access-Control-Allow-Headers

    做用:代表它容許跨域請求包含指定的那些頭信息

    使用格式:Access-Control-Allow-Headers: "Access-Control-Allow-Headers" ":" #field-name
  6. Access-Control-Request-Method

    做用:代表它容許使用哪些HTTP方法跨域請求

    使用格式:Access-Control-Request-Method: "Access-Control-Request-Method" ":" Method
  7. Access-Control-Request-Headers

    做用:代表它容許跨域請求設置哪些頭信息

    使用格式:Access-Control-Request-Headers: "Access-Control-Request-Headers" ":" #field-name
(function (global, undefined) {
    //根據有無withCredentials來判斷瀏覽器時候支持XMLHttpRequest跨域請求操做
    var XMLHttpRequestCORS = (function () {
        if (!('XMLHttpRequest' in global))
            return false;
        var a = new XMLHttpRequest();
        return a.withCredentials !== undefined;
    })(), request = function () {
        //判斷瀏覽器兼容性
        if ('XDomainRequest' in global)
            return new XDomainRequest();
        //是否支持跨域請求
        if ('XMLHttpRequest' in global && XMLHttpRequestCORS)
            return new XMLHttpRequest();
        return false;
    };
    var xhr = request();
    if(xhr){
        xhr.open("get", "http://localhost:1111");
        xhr.onload = function () {
            //onload方法表示請求已經完成,參見上面XMLHttpRequest的onload屬性解釋
            console.log(this.responseText);
        };
        xhr.send();
    }

})(window);

結束語

如今你可能已經準備開始編寫第一個Ajax應用程序了,不過能夠首先從這些應用程序如何工做的基本概念開始,對XMLHttpRequest對象有基本的瞭解。

如今先花點兒時間考慮考慮Ajax應用程序有多麼強大。設想一下,當單擊按鈕、輸入一個字段、從組合框中選擇一個選項或者用鼠標在屏幕上拖動時,Web 表單可以馬上做出響應會是什麼情形。想想異步 究竟意味着什麼,想想 JavaScript 代碼運行並且不等待 服務器對它的請求做出響應。會遇到什麼樣的問題?會進入什麼樣的領域?考慮到這種新的方法,編程的時候應如何改變表單的設計?

若是在這些問題上花一點兒時間,與簡單地剪切/粘貼某些代碼到您根本不理解的應用程序中相比,收益會更多。

參考

  • http://bob.ippoli.to/archives/2005/12/05/remote-json-jsonp/
  • http://www.w3.org/TR/cors/
  • http://www.w3.org/TR/XMLHttpRequest/
  • https://msdn.microsoft.com/zh-cn/library/ie/cc288060(v=vs.85).aspx
  • https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
  • https://developer.mozilla.org/en-US/docs/Web/HTTP
相關文章
相關標籤/搜索