使用JS和Ajax發出異步請求

本文介紹瞭如何建立可以適應不一樣瀏覽器的XMLHttpRequest實例,創建和發送請求,並響應服務器。您將開始接觸最基本和基礎性的有關Ajax的所有對象和編程方法:XMLHttpRequest對象。該對象實際上僅僅是一個跨越全部Ajax應用程序的公共線程,您可能已經預料到,只有完全理解該對象才能充分發揮編程的潛力。 
XMLHttpRequest簡介
javascript

XMLHttpRequest 是JS的一個對象。它是介紹 Web 2.0、Ajax 和大部分其餘內容的核心。下面給出該對象的幾個方法和屬性:
一、open():創建到服務器的新請求。 
二、send():向服務器發送請求。 
三、abort():退出當前請求。 
四、readyState:提供當前 HTML 的就緒狀態。 
五、responseText:服務器返回的請求響應文本。
用XMLHttpRequest可以作什麼呢,值得注意的是這些方法和屬性都與發送請求及處理響應有關。事實上,若是看到XMLHttpRequest的全部方法和屬性,就會發現它們都與很是簡單的請求/響應模型有關。用好該對象能夠完全改變您的應用程序。
 建立XMLHttpRequest對象實例
首先須要建立一個新變量並賦給它一個XMLHttpRequest對象實例。這在JS中很簡單,只要對該對象名使用new關鍵字便可.建立新的XMLHttpRequest對象:var request = new XMLHttpRequest(); 
建立 XMLHttpRequest的Java僞代碼:XMLHttpRequest request = new XMLHttpRequest(); 
錯誤與跨瀏覽器處理
在實際上各類事情均可能出錯,而上面的代碼沒有提供任何錯誤處理。較好的辦法是建立該對象,並在出現問題時優雅地退出。好比,任何較早的瀏覽器都不支持 XMLHttpRequest,您須要讓這些用戶知道有些地方出了問題。下面說明如何建立該對象,以便在出現問題的時候發出 JavaScript 警告。
建立具備錯誤處理能力的XMLHttpRequest對象
<script language="javascript" type="text/javascript">
var request = false;
try {
 request = new XMLHttpRequest();
} catch (failed) {
 request = false;
}
if (!request)
 alert("Error initializing XMLHttpRequest!");
</script> 
必定要理解這些步驟: 
一、建立一個新變量 request 並賦值 false。後面將使用 false 做爲斷定條件,它表示尚未建立 XMLHttpRequest 對象。 
二、增長 try/catch 塊: 
三、嘗試建立 XMLHttpRequest 對象。 
四、若是失敗(catch (failed))則保證 request 的值仍然爲 false。 
五、檢查 request 是否仍爲 false(若是一切正常就不會是 false)。 
六、若是出現問題(request 是 false)則使用 JavaScript 警告通知用戶出現了問題。 
如今已經獲得了一段帶有錯誤檢查的XMLHttpRequest對象建立代碼,還能夠告訴您哪兒出了問題。 
增長對Microsoft瀏覽器的支持
<script language="javascript" type="text/javascript">
var request = false;
try {
 request = new XMLHttpRequest();
} catch (trymicrosoft) {
 try {
    request = new ActiveXObject("Msxml2.XMLHTTP");
 } catch (othermicrosoft) {
    try {
      request = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (failed) {
      request = false;
    }
 }
}
if (!request)
 alert("Error initializing XMLHttpRequest!");
</script> 
下面分別介紹每一步:
一、建立一個新變量 request 並賦值 false。使用 false 做爲判斷條件,它表示尚未建立 XMLHttpRequest 對象。 
二、增長 try/catch 塊: 
三、嘗試建立XMLHttpRequest對象。
四、檢查 request 是否仍然爲 false(若是一切順利就不會是 false)。 
五、若是出現問題(request 是 false)則使用 JavaScript 警告通知用戶出現了問題。 
這樣修改代碼以後再使用InternetExplorer試驗,就應該看到已經建立的表單(沒有錯誤消息)。
靜態代碼與動態代碼
代碼都直接嵌套在script標記中,不放到方法或函數體中的JS代碼稱爲靜態JS。這種狀況代碼是在頁面顯示給用戶以前的某個時候運行。雖然根據規範不能徹底精確地知道這些代碼什麼時候運行對瀏覽器有什麼影響,可是能夠保證這些代碼在用戶可以與頁面交互以前運行,這也是多數Ajax程序員建立XMLHttpRequest對象的通常方式。
將 XMLHttpRequest建立代碼移動到方法中 
<script language="javascript" type="text/javascript">
var request;
function createRequest() {
 try {
    request = new XMLHttpRequest();
 } catch (trymicrosoft) {
    try {
      request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (othermicrosoft) {
      try {
        request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (failed) {
        request = false;
      }
    }
 }
 if (!request)
    alert("Error initializing XMLHttpRequest!");
}
</script> 
若是按照這種方式編寫代碼,那麼在處理 Ajax 以前須要調用該方法。所以還須要使用XMLHttpRequest的建立方法。
function getCustomerInfo() {
 createRequest();
}
此方式唯一的問題是推遲了錯誤通知,這也是多數 Ajax 程序員不採用這一方法的緣由。若是使用靜態JS,用戶在點擊頁面的時候很快就會看到錯誤信息。這樣也很煩人,是否是?可能令用戶錯誤地認爲您的Web應用程序不能在他的瀏覽器上運行。不過,固然要比他們花費了10 分鐘輸入信息以後再顯示一樣的錯誤要好。所以,我建議編寫靜態的代碼,讓用戶儘量早地發現問題。 
用XMLHttpRequest發送請求
獲得請求對象以後就能夠進入請求/響應循環了。記住XMLHttpRequest 唯一的目的是讓您發送請求和接收響應。其餘一切都是JS、CSS 或頁面中其餘代碼的工做。如改變用戶界面、切換圖像、解釋服務器返回的數據。準備好XMLHttpRequest 以後,就能夠向服務器發送請求了。Ajax 採用一種沙箱安全模型。所以Ajax 代碼(具體來講就是XMLHttpRequest對象)只能對所在的同一個域發送請求。記住:在本地機器上運行的代碼只能對本地機器上的服務器端腳本發送請求。如讓Ajax代碼在
www.google.com上運行,則必須在www.google.com中運行的腳本發送請求。
設置服務器URL
首先要肯定鏈接的服務器的 URL。這並非Ajax的特殊要求,但仍然是創建鏈接所必需的,顯然如今您應該知道如何構造URL了。多數應用程序中都會結合一些靜態數據和用戶處理的表單中的數據來構造該URL。 
創建請求URL
<script language="javascript" type="text/javascript">
   var request = false;
   try {
     request = new XMLHttpRequest();
   } catch (trymicrosoft) {
     try {
       request = new ActiveXObject("Msxml2.XMLHTTP");
     } catch (othermicrosoft) {
       try {
         request = new ActiveXObject("Microsoft.XMLHTTP");
       } catch (failed) {
         request = false;
       } 
     }
   }
   if (!request)
     alert("Error initializing XMLHttpRequest!");
   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
   }
</script> 
首先代碼建立了一個新變量phone,並把ID爲「phone」 的表單字段的值賦給它。下面展現了這個表單的HTML,其中能夠看到phone字段及其id屬性。 
 <body>
 <p><img src="breakneck-logo_4c.gif" alt="Break Neck Pizza" /></p>
 <form action="POST">
   <p>Enter your phone number:
    <input type="text" size="14" name="phone" id="phone" 
           onChange="getCustomerInfo();" />
php

span>
   </p>
   <p>Your order will be delivered to:</p>
   <div id="address"></div>
   <p>Type your order in here:</p>
   <p><textarea name="order" rows="6" cols="50" id="order"></textarea></p>
   <p><input type="submit" value="Order Pizza" id="submit" /></p>
 </form>
 </body> 
還要注意,當用戶輸入電話號碼或者改變電話號碼時,將觸發getCustomerInfo() 方法。該方法取得電話號碼並構造存儲在url變量中的 URL字符串。記住,因爲Ajax代碼是沙箱型的,於是只能鏈接到同一個域,實際上URL中不須要域名。
escape() 方法是一個頂級JS方法,並不與任何對象關聯。使用escape方法能夠將屬性值手工添加到URL中。escape方法編碼指定字符串中的特定字符,並返回新字符串。它用於轉義不能用明文正確發送的任何字符。好比電話號碼中的空格將被轉換成字符%20,從而可以在URL中傳遞這些字符。 
能夠根據須要添加任意多個參數。好比須要增長另外一個參數,只須要將其附加到URL中並用「與」(&)字符分開,第一個參數用問號(?)和腳本名分開。 
打開請求
open() 是打開嗎?Internet 開發人員對open() 方法到底作什麼沒有達成一致。但它實際上並非打開一個請求。若是監控 HTML/Ajax 頁面及其鏈接腳本之間的網絡和數據傳遞,當調用 open() 方法時將看不到任何通訊。不清楚爲什麼選用了這個名字,但顯然不是一個好的選擇。有了要鏈接的URL後就能夠配置請求了。能夠用XMLHttpRequest對象的open()方法來完成。該方法有五個參數:
一、request-type:發送請求的類型。典型的值是 GET 或 POST,但也能夠發送 HEAD 請求。 
二、url:要鏈接的 URL。 
三、asynch:若是但願使用異步鏈接則爲 true,不然爲 false。該參數是可選的,默認爲 true。 
四、username:若是須要身份驗證,則能夠在此指定用戶名。該可選參數沒有默認值。 
五、password:若是須要身份驗證,則能夠在此指定口令。該可選參數沒有默認值。 
一般使用其中的前三個參數。事實上即便須要異步鏈接,也應該指定第三個參數爲true。這是默認值,但堅持明確指定請求是異步的仍是同步的更容易理解。 
function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
 } 
一旦設置好了URL,其餘就簡單了。多數請求使用GET就夠了,再加上URL,這就是使用open() 方法須要的所有內容了。
挑戰異步性
編寫和使用異步代碼,您應該明白爲何open() 的最後一個參數這麼重要。在通常的請求/響應模型中,好比 Web 1.0,客戶機(瀏覽器或者本地機器上運行的代碼)向服務器發出請求。該請求是同步的,換句話說客戶機等待服務器的響應。當客戶機等待的時候,至少會用某種形式通知您在等待:
一、沙漏(特別是 Windows 上)。 
二、旋轉的皮球(一般在 Mac 機器上)。 
三、應用程序基本上凍結了,而後過一段時間光標變化了。
這正是Web應用程序讓人感到笨拙或緩慢的緣由——缺少真正的交互性。按下按鈕時,應用程序實際上變得不能使用,直到剛剛觸發的請求獲得響應。若是請求須要大量服務器處理,那麼等待的時間可能很長。而異步請求不等待服務器響應。發送請求後應用程序繼續運行。用戶仍然能夠在Web表單中輸入數據,甚至離開表單。沒有旋轉的皮球或者沙漏,應用程序也沒有明顯的凍結。服務器悄悄地響應請求,完成後告訴原來的請求者工做已經結束。結果是應用程序感受不那麼遲鈍或者緩慢,而是響應迅速、交互性強,感受快多了。這僅僅是Web 2.0的一部分,但它是很重要的一部分。全部Web2.0以前的GUI組件和Web設計都不能克服緩慢、同步的請求/響應模型。
 發送請求
一旦用 open() 配置好以後,就能夠發送請求了。幸運的是,發送請求的方法的名稱要比open() 適當,它就是send()方法。send()方法只有一個參數,就是要發送的內容。可是在考慮這個方法以前,回想一下前面已經經過URL自己發送過的數據了。
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone); 
雖然可使用send()發送數據,但也能經過URL自己發送數據。事實上,GET請求(在典型的Ajax 應用中大約佔80%)中,用URL發送數據要容易得多。若是須要發送安全信息或XML,可能要考慮使用send()發送內容。若是不須要經過send()傳遞數據,則只要傳遞null 做爲該方法的參數便可。所以您會發如今本文中的例子中只須要這樣發送請求。
 function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
     request.send(null);
 } 
指定回調方法
如今咱們所作的只有不多一點是新的、革命性的或異步的。必須認可,open()方法中true這個小小的關鍵字創建了異步請求。可是除此以外,這些代碼與用 Java servlet 及 JSP、PHP 或 Perl 編程沒有什麼兩樣。那麼Ajax和Web 2.0最大的祕密是什麼呢?祕密就在於 XMLHttpRequest的一個簡單屬性onreadystatechange。
首先必定要理解這些代碼中的流程。創建其請求而後發出請求。此外,由於是異步請求,因此JavaScript 方法(例子中的 getCustomerInfo())不會等待服務器。所以代碼將繼續執行,就是說,將退出該方法而把控制返回給表單。用戶能夠繼續輸入信息,應用程序不會等待服務器。這就提出了一個有趣的問題:服務器完成了請求以後會發生什麼?答案是什麼也不發生,至少對如今的代碼而言如此!顯然這樣不行,所以服務器在完成經過 XMLHttpRequest 發送給它的請求處理以後須要某種指示說明怎麼作。 
在JS中引用函數
JavaScript 是一種弱類型的語言,能夠用變量引用任何東西。所以若是聲明瞭一個函數 updatePage(),JavaScript 也將該函數名看做是一個變量。換句話說,可用變量名updatePage在代碼中引用函數。 如今onreadystatechange屬性該登場了。該屬性容許指定一個回調函數。回調容許服務器反向調用 Web 頁面中的代碼。它也給了服務器必定程度的控制權,當服務器完成請求以後,會查看XMLHttpRequest 對象,特別是onreadystatechange屬性。而後調用該屬性指定的任何方法。之因此稱爲回調是由於服務器向網頁發起調用,不管網頁自己在作什麼。比方說,可能在用戶坐在椅子上手沒有碰鍵盤的時候調用該方法,可是也可能在用戶輸入、移動鼠標、滾動屏幕或者點擊按鈕時調用該方法。它並不關心用戶在作什麼。這就是稱之爲異步的緣由:用戶在一層上操做表單,而在另外一層上服務器響應請求並觸發 onreadystatechange 屬性指定的回調方法。所以須要像以下同樣在代碼中指定該方法。
 設置回調方法
 function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
 } 
特別注意:該屬性在代碼中設置的位置 —— 它是在調用 send() 以前設置的。發送請求以前必須設置該屬性,這樣服務器在回答完成請求以後才能查看該屬性。如今剩下的就只有編寫 updatePage() 方法了。 
處理服務器響應
發送請求,用戶高興地使用 Web 表單(同時服務器在處理請求),而如今服務器完成了請求處理。服務器查看 onreadystatechange 屬性肯定要調用的方法。除此之外,能夠將您的應用程序看做其餘應用程序同樣,不管是否異步。換句話說,不必定要採起特殊的動做編寫響應服務器的方法,只須要改變表單,讓用戶訪問另外一個 URL 或者作響應服務器須要的任何事情。這裏咱們重點討論對服務器的響應和一種典型的動做 —— 即時改變用戶看到的表單中的一部分。 
回調和Ajax
如今咱們已經看到如何告訴服務器完成後應該作什麼:將 XMLHttpRequest 對象的onreadystatechange 屬性設置爲要運行的函數名。這樣,當服務器處理完請求後就會自動調用該函數。也不須要擔憂該函數的任何參數。咱們從一個簡單的方法開始,回調方法的代碼
 <script language="javascript" type="text/javascript">
   var request = false;
   try {
     request = new XMLHttpRequest();
   } catch (trymicrosoft) {
     try {
       request = new ActiveXObject("Msxml2.XMLHTTP");
     } catch (othermicrosoft) {
       try {
         request = new ActiveXObject("Microsoft.XMLHTTP");
       } catch (failed) {
         request = false;
       } 
     }
   }
   if (!request)
     alert("Error initializing XMLHttpRequest!");
   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url
java

= "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }
   function updatePage() {
     alert("Server is done!");
   }
</script> 
它僅僅發出一些簡單的警告,告訴您服務器何時完成了任務。在本身的網頁中試驗這些代碼,而後在瀏覽器中打開(若是但願查看該例中的 XHTML,請參閱 清單 8)。輸入電話號碼而後離開該字段,將看到一個彈出的警告窗口,可是點擊 OK 又出現了……
根據瀏覽器的不一樣,在表單中止彈出警告以前會看到兩次、三次甚至四次警告。這是怎麼回事呢?原來咱們尚未考慮 HTTP 就緒狀態,這是請求/響應循環中的一個重要部分。 
HTTP 就緒狀態
前面提到,服務器在完成請求以後會在 XMLHttpRequest 的 onreadystatechange 屬性中查找要調用的方法。這是真的,但還不完整。事實上,每當 HTTP 就緒狀態改變時它都會調用該方法。這意味着什麼呢?首先必須理解 HTTP 就緒狀態。HTTP 就緒狀態表示請求的狀態或情形。它用於肯定該請求是否已經開始、是否獲得了響應或者請求/響應模型是否已經完成。它還能夠幫助肯定讀取服務器提供的響應文本或數據是否安全。在 Ajax 應用程序中須要瞭解五種就緒狀態:
0:請求沒有發出(在調用 open() 以前)。 
1:請求已經創建但尚未發出(調用 send() 以前)。 
2:請求已經發出正在處理之中(這裏一般能夠從響應獲得內容頭部)。
3:請求已經處理,響應中一般有部分數據可用,可是服務器尚未完成響應。
4:響應已完成,能夠訪問服務器響應並使用它。
 
與大多數跨瀏覽器問題同樣,這些就緒狀態的使用也不盡一致。您也許指望任務就緒狀態從0到一、二、3 再到4,但實際上不多是這種狀況。一些瀏覽器從不報告0或1而直接從2開始,而後是3和4。其餘瀏覽器則報告全部的狀態。還有一些則屢次報告就緒狀態 1。在上一節中看到,服務器屢次調用updatePage(),每次調用都會彈出警告框——可能和預期的不一樣! 
對於 Ajax 編程,須要直接處理的唯一狀態就是就緒狀態 4,它表示服務器響應已經完成,能夠安全地使用響應數據了。基於此,回調方法中的第一行應該以下檢查就緒狀態 
function updatePage() {
     if (request.readyState == 4)
       alert("Server is done!");
}
修改後就能夠保證服務器的處理已經完成。嘗試運行新版本的Ajax代碼,如今就會看到與預期的同樣,只顯示一次警告信息了。
HTTP 狀態碼 
雖然代碼看起來彷佛不錯,可是還有一個問題——若是服務器響應請求並完成了處理可是報告了一個錯誤怎麼辦?要知道,服務器端代碼應該明白它是由Ajax、JSP、普通HTML表單或其餘類型的代碼調用的,但只能使用傳統的Web專用方法報告信息。而在Web世界中,HTTP代碼能夠處理請求中可能發生的各類問題。比方說,您確定遇到過輸入了錯誤的 URL 請求而獲得 404 錯誤碼的情形,它表示該頁面不存在。這僅僅是 HTTP 請求可以收到的衆多錯誤碼中的一種(完整的狀態碼請參閱相關資料)。表示所訪問數據受到保護或者禁止訪問的 403 和 401 也很常見。不管哪一種狀況,這些錯誤碼都是從完成的響應 獲得的。換句話說,服務器履行了請求(即 HTTP 就緒狀態是 4)可是沒有返回客戶機預期的數據。所以除了就緒狀態外,還須要檢查 HTTP 狀態。咱們指望的狀態碼是 200,它表示一切順利。若是就緒狀態是 4 並且狀態碼是 200,就能夠處理服務器的數據了,並且這些數據應該就是要求的數據(而不是錯誤或者其餘有問題的信息)。所以還要在回調方法中增長狀態檢查,檢查 HTTP 狀態碼。 
function updatePage() {
     if (request.readyState == 4)
       if (request.status == 200)
         alert("Server is done!");
}
爲了增長更健壯的錯誤處理並儘可能避免過於複雜,能夠增長一兩個狀態碼檢查,請看一看清單 15 中修改後的 updatePage() 版本。
增長一點錯誤檢查
function updatePage() {
     if (request.readyState == 4)
       if (request.status == 200)
         alert("Server is done!");
       else if (request.status == 404)
         alert("Request URL does not exist");
       else
         alert("Error: status code is " + request.status);
}
如今將 getCustomerInfo() 中的 URL 改成不存在的 URL 看看會發生什麼。應該會看到警告信息說明要求的 URL 不存在 —— 好極了!很難處理全部的錯誤條件,可是這一小小的改變可以涵蓋典型 Web 應用程序中 80% 的問題。 
讀取響應文本
如今能夠確保請求已經處理完成(經過就緒狀態),服務器給出了正常的響應(經過狀態碼),最後咱們能夠處理服務器返回的數據了。返回的數據保存在 XMLHttpRequest 對象的 responseText 屬性中。關於 responseText 中的文本內容,好比格式和長度,有意保持含糊。這樣服務器就能夠將文本設置成任何內容。比方說,一種腳本可能返回逗號分隔的值,另外一種則使用管道符(即 | 字符)分隔的值,還有一種則返回長文本字符串。何去何從由服務器決定。在本文使用的例子中,服務器返回客戶的上一個訂單和客戶地址,中間用管道符分開。而後使用訂單和地址設置表單中的元素值,下面給出了更新顯示內容的代碼,處理服務器響應。 
function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
           response[1].replace(/\n/g, "");
       } else
         alert("status is " + request.status);
     }
}
首先獲得responseText並使用JS的split() 方法從管道符分開。獲得的數組放到response中。數組中的第一個值——上一個訂單——用 response[0] 訪問,被設置爲ID 爲「order」 的字段的值。第二個值 response[1],即客戶地址,則須要更多一點處理。由於地址中的行用通常的行分隔符(「\n」字符)分隔,代碼中須要用 XHTML 風格的行分隔符 <br /> 來代替。替換過程使用 replace() 函數和正則表達式完成。最後,修改後的文本做爲 HTML 表單 div 中的內部 HTML。
程序員

responseXML屬性
如今介紹XMLHttpRequest 的另外一個重要屬性responseXML。若是服務器選擇使用 XML 響應則該屬性包含(也許您已經猜到)XML 響應。處理 XML 響應和處理普通文本有很大不一樣,涉及到解析、文檔對象模型(DOM)和其餘一些問題。對於不少簡單的 Ajax 應用程序 responseText 就夠了,可是其實經過 Ajax 應用程序也能很好地處理 XML。
web

結束語
您可能對XMLHttpRequest感到有點厭倦了,我不多看到一整篇文章討論一個對象,特別是這種簡單的對象。可是您將在使用Ajax編寫的每一個頁面和應用程序中反覆使用該對象。坦白地說,關於 XMLHttpRequest 還真有一些可說的內容。其實有不少Ajax工具箱。這些工具箱實際上隱藏了本文所述的不少細節,使得 Ajax 編程更容易。您也許會想,既然有這麼多工具箱爲什麼還要對底層的細節編碼。答案是你若是不知道應用程序在作什麼,就很難發現應用程序中的問題。所以不要忽略這些細節或者簡單地瀏覽一下,若是便捷華麗的工具箱出現了錯誤,您就沒必要撓頭或者發送郵件請求支持了。若是瞭解如何直接使用 XMLHttpRequest,就會發現很容易調試和解決最奇怪的問題。只有讓其解決您的問題,工具箱纔是好東西。所以請熟悉 XMLHttpRequest 吧。事實上,若是您有使用工具箱的 Ajax 代碼,能夠嘗試使用 XMLHttpRequest 對象及其屬性和方法從新改寫。這是一種不錯的練習,能夠幫助您更好地理解其中的原理。 
正則表達式

相關文章
相關標籤/搜索