JavaScript - 程式碼壓縮與最佳化

壓縮程式碼

在正式上線前,可以將JavaScript程式碼進行壓縮,減少檔案大小,讓使用者下載更快,也節省頻寬。方法有手動與自動兩種。

手動(效果不好):

  • 刪除「長變數名」、「註解」、「空格」、「換行」…
  • true改用1, false改用0

缺點,易產生錯誤。

自動(建議使用):

使用工具,自動化產生壓縮式的JavaScript檔案:


JavaScript自動化壓縮工具還有很多,各位可以自行上網找一套自己喜歡的來用。未來新的 Visual Studio 11 / .NET Framework 4.5 會將自動化壓縮最佳化 JavaScript 與 CSS 等特性加入。

JavaScript 程式碼最佳化

這個部份提出的都是自動化壓縮工具比較幫不上忙,有些是邏輯技巧,有些是程式技巧,順序上,應該先進行內部程式碼最佳化,然後再使用壓縮工具來加速提升。

宣告變數

JavaScript中未宣告的變數(函式中也一樣)預設為全域變數,全域變數只有在瀏覽器關閉後才釋放。

name = Bruce;

使用內建函數

內建函數都已編譯過,執行速度會比即時編譯的JavaScript快很多。

巢狀if

將出現率最多的情況放在前面,另外,使用switch語句替代巢狀if/else if。

整合同性質工作

指令碼中程式行數越少,執行時間越快,將同性質的指令碼集中一次處理,例如,例如宣告變數:

// 宣告方法一
var name="Bruce";
var age = 18;
var birthday = new Date(2010/10/10);

// 宣告方法二
var name="Bruce", age=18,birthday=new Date(2010/10/10);

節約使用DOM

JavaScript對DOM的處理可能是最耗時費力,盡量減少DOM的操作,例如,為ul新增10個li,我們的處理方式可以有:

var oUL = document.getElementById("ulItem");
for (var i=0; i<10; i++){
  var oLI = document.createElement("li");
  oUL.appendChild(oLI); //10次
  oLI.appendChild(document.createTextNode("Item" + i)); //10次
}

共執行20次DOM操作。其實我們可以有更好的選擇:

var oUL = document.getElementById("ulItem");
var oTemp = document.createDocumentFragment();
for (var i=0; i<10; i++){
  var oLI = document.createElement("li");
  oLI.appendChild(document.createTextNode("Item" + i));
  oTemp.appendChild(oLI);
}
oUL.appendChild(oTemp); //1次

共執行1次DOM操作。20 : 1,相信那段程式碼效率一眼就可以看出來。

[KKBruce補充]可多利用 document.createDocumentFragment 來最佳化相關程式碼。

最佳化的影響

壓縮程式碼與程式碼最佳化通常會大大影響「可讀性」,不佳的可讀性讓後續「維護」困難度升高,所以正常做法是分為上線和開發兩個版本。

  • 上線:使用壓縮及最佳化,加速下載速度,提高執行效能。
  • 開發:保持良好程式架構和註解等,提高可讀性及減低維護難度。

JavaScript - 除錯, Debug

常見錯誤有:
  1. 語法錯誤(syntax error)
  2. 邏輯錯誤(logic error)
  3. 執行錯誤(runtime error)

除蟲清單:
  • 確定「括號( )」成對出現
  • 確定程式區塊的「大括號{ }」成對出現
  • 避免打錯字,使用有關鍵字提示功能IDE開發工具
  • JavaScript有大小寫之分
  • 字串與變數連接錯誤,例如,多或少+連接符號
  • 單、雙引號,維持一致使用方式
  • 單、雙引號,沒有成對出現也是常見錯誤
  • 確定物件在使用前已經建立
  • 別對區域變數與全域變數取相同的名稱
  • 變數不存在
  • 等號(=)與比較符號(==)使用錯誤
  • 記得要結束";"

bug處理:
  • alert()與document.write()方法
  • 使用window.onerror事件
  • try ... catch
  • Firebug for Firefox

目前主流且最新版本的瀏覽器(IE9, Chrome 16)都內建不錯的除錯功能,也是另一種選擇。

JavaScript - 物件導向(Object-oriented Language, OOP)

Object-oriented Language

Object-oriented Language,物件導向程式語言,特性有:

  1. 封裝Encapsulation
  2. 繼承Inheritance
  3. 多形Polymorphism

Object(物件)

讓正真有需要的原始碼接觸資料,減少資料曝露的程度,讓程式碼再利用。物件結合資料與行為,建立一種新的資料型別,其中可以儲存(store)資料,也可以根據資料而行動(act),所以物件是個容器,其中儲存資料,並聯繫資料與依據資料行動的原始碼。物件(Object)在一個儲存容器內聯繫起變數與函式。

資料(變數)+行為(函式)=物件

物件內的資料稱「property」(屬性、特性),物件內的行為稱「method」(方法),

property + method = Object

.(點)

點號運算子('.' dot operator)用於取用物件property與method,

Object + . + property/method

例如,

// mail 物件
// . 運算子
// from, to, send 方法
mail.from = "...";
mail.to = "...";
...
mail.send();

物件可以包含其他物件,例如,

var mail = new Mail(new Date(), "from", ...);
在Mail建構式中傳入一個Date物件。

建構式(constructor) - 屬性(property)

物件具有資料,資料必須在物件建立時「初始化」,每個自訂物件都需要自己的建構式,名稱與「物件相同」。

function Mail(from,to,body,subject){
  this.from = from;
  this.to = to;
  this.body = body;
  this.subject = subject;
}

建構式使用首字母大寫,建立property需使用JavaScript的關鍵字「this」,關鍵字this區分物件property與一般變數。this指定物件property的擁有權,同時設定property的初始值建立屬於「這個(this)」物件的property,例如,

this.from = from;

將收到的參數指定給新property。沒有this,建構式不會知道你正在建立物件property。以建構式建立物件時,我們使用new運算子,它呼叫物件的建構式,開啟物件建立過程。例如,

var mail = new Mail("from","to","body","subject");

  1. var mail:新物件儲存在變數裡
  2. new Mail(...):new運算子建立新Mail物件(呼叫建構式)
  3. "from","to","body",...:傳入參數給建構式,以設定property

建構式(constructor) - 方法(method)

function Mail(from,to,body,subject){
  // Object method
  this.send = function(){
  ...
  }
}

該如何知道指令碼(函式)應放入method?

method就是根據物件的property而採取某些行動,換言之,如果指令碼需要取用物件內部資料,就非常合適放入method。

把函式轉為方法

  1. 宣告method
  2. 把現有程式碼移入method裡
  3. 修改使用物件property

宣告method,使用this建立method,與建立property類似。例如,

this.send = function(){
  // Do something
}

關於選用參數

function Mail(from,to,body,subject,smtp,port){
  ...
  // 假設smtp與port為選用功能
  this.smtp = smtp;
  this.port = port;
}

當某個參數未傳入給function、method、constructor時,在任何試圖使用參數值的原始碼裡,它的值都是null,在缺少參數相對應的property將會被設為null。確認選用參數在function參數清單的最後面。例如:當funcion有兩個參數,可能的選擇有:

  • 兩個都傳入
  • 只傳入第一個
  • 兩個都不傳
  • 無法只傳第二個

複本

Mail物件的send()方法,Mail物件的method是在建構式裡,利用關鍵字this建立,但依此建立的Mail物件都會各有一份物件方法的複本,關鍵字"this"用於設置「instance」擁有的property和method。例如,

var mail1 = new Mail(...);
var mail2 = new Mail(...);
var mail3 = new Mail(...);

每個Mail物件(mail1,mail2,mail3),都會有自己的property與method複本,等於會有三份method,這是浪費又沒效率。property為每個物件儲存獨特資料,method應該為物件所共享。

Class與instance

類別Class,是物件的描述,樣版、藍圖。例如,

function Mail(...)

實例Instance,實際物件,依Class建立。例如,

var mail = new Mail(...);
mail是依Mail來建立的物件(instance)。

每個instance的property多半都不一樣,所以每個instance才需要有自己的property複本,但method沒有必要複製到每個instance中。

Class擁有method

class-owned instance method,當method為Class所擁有時,所有該Class的instance都可以取用此method,而不需要另外複製一份。

在Class使用prototype

在JavaScript中每個物件都有一個prototype物件(以property存在),prototype用以設定Class level(類別層級)的property與method, 而非屬於instance。也就是說,我們需要把method的「擁有權」指派給Class,而不是指派小單一instance,例如,

Mail.prototype.send = function(){}

prototype使用需在建構式之外。send()方法直接附加到Mail class本身,而非隸屬於某個instance,這樣無論Mail class建立多少份instance,都只會有一份send()方法。當send()接受呼叫時,將在class內執行,例如,

mail1.send();
mail2.send();
mail3.send();

當其他Mail物件的instance也呼叫了send()方法,仍會執行Class內的同一方法。

class property
類別屬性,此property只需要在類別儲存一次,但能被所有instance存取,例如,

Mail.prototype.smtp = "smtp.kkbruce.net";
Mail.prototype.port = "25";

此property只有一個值,讓所有instance共享。想成,它是class的全域變數,但只能讓class對應的instance取用。JavaScript標準物件能也使用prototype,每個物件都有prototype,它允許在Class Level增加property與method,例如,擴充Date物件,

Date.prototype.shortFormat = function(){
  return this.getFullYear() +'/'+(this.getMonth()+1)+'/'+ this.getDate();
}

var d = new Date();
d.shortFormat();
這樣即可傳回「年/月/日」格式日期函式。

Class自己的method

類別方法(class method),它屬於Class,只能取用class property,不能取用instance property,建立class method是直接為Class設定method,而不使用prototype,例如,

Mail.showSMTP = function(){
  alert('SMTP:'+Mail.prototype.smtp+' ,\n Port:'+Mail.prototype.port);
}
...
Mail.showSMTP();
  • Mail.showSMTP:建立Mail物件的showSMTP方法
  • Mail.prototype.smtp:為了從class method取用class property,你必須下探至prototype,smtp是個Mail class property,所以Mail class method可以取用
  • Mail.showSMTP();:直接透過Class名稱Mail來呼叫方法

再回建構式

讓建構式專注在property的建立與初始化 OOP範例:AJAX OOP的寫法

function AjaxRequest() {
  if (window.XMLHttpRequest) {
    try {
      this.request = new XMLHttpRequest();
    } catch(e) {
      this.request = null;
    }
  } else if (window.ActiveXObject) {
      try {
        this.request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {
        this.request = null;
      }
  }

if (this.request == null)
    alert("建立Ajax物件錯誤.\n" + "細節:" + e.message);
}

AjaxRequest.prototype.send = function(type, url, handler, postDataType, postData) {
  //type, url, handler為get必要參數
  //postDataType, postData為post必要參數
  if (this.request != null) {
    this.request.abort();

    url += "?dummy=" + new Date().getTime();

    try {
      this.request.onreadystatechange = handler;
      this.request.open(type, url, true); 
      if (type.toLowerCase() == "get") {
        this.request.send(null);
      } else {
        this.request.setRequestHeader("Content-Type", postDataType);
        this.request.send(postData);
      }
    } catch(e) {
      alert("Ajax程式錯誤.\n" + "細節:" + e);
    }
  }
}

AjaxRequest.prototype.getReadyState = function() {
  return this.request.readyState;
}

AjaxRequest.prototype.getStatus = function() {
  return this.request.status;
}

AjaxRequest.prototype.getResponseText = function() {
  return this.request.responseText;
}

AjaxRequest.prototype.getResponseXML = function() {
  return this.request.responseXML;
}

宣告AjaxRequest物件,

var ajax = new AjaxRequest();

JavaScript - Asynchronous JavaScript And XML, AJAX

同步與非同步

同步HTTP請求:請求 → 顯示 → 互動 → 暫停 → 請求 → 顯示 → 互動 → 暫停 …

需要不斷重新刷新整個頁面,會出現一段時間的空白。

非同步HTTP請求:請求 → 顯示 → 互動 → (請求)互動 → (請求)互動 …

非同步HTTP請求在顯示、執行互動不需要再暫停操作,可以馬上執行所需的互動,只向伺服器要求一小部份資料,然後更新頁面的一小部份, 不需要重新刷新整個頁面,也不會出現一段時間的空白。

非同步好處

  1. 減輕伺服器的負擔,加快瀏覽速度(只給所需)
  2. 帶來更好的用戶體驗(更流暢的畫面)
  3. 標準化技術,主流瀏覽器都支援
  4. 促進頁面呈現與資料分離(提高可維護性)

AJAX組成

AJAX不是單一技術,是集4種技術而成,JavaScript、CSS、DOM、XMLHttpRequest。(或說5種,再加上(X)HTML)

JavaScript AJAX技術組成
名稱說明
JavaScript通用指令碼語言。是撰寫AJAX主要程式語言
CSS為Web頁面元素提供視覺化樣式的定義方法, AJAX透過修改CSS樣式,改變使用者介面
DOMAJAX可以在執行時透過DOM來改變使用者介面,或局部更新某個節點
XMLHttpRequestXMLHttpRequest物件允許Web開發人員向伺服器以非同步方式取資料, 資料格式通常是XML或是文字,目前較流行為JSON

XMLHttpRequest物件

能夠直接在Client使用Javascript來提出HTTP請求。IE內建MSXML剖析器有多個版本,其提供的XMLHttpReqest物件也有多個版本(不建議使用)。

KKBruce提醒,除非您必須處理類似IE等相容性問題,不然是不建議使用以下表格內容。

JavaScript IE內建MSXML剖析器
版本DLL名稱ProgID
2.0(IE5)msxml.dllMicrosoft.XMLHttp
2.6msxml2.dllMSXML2.XMLHttp
3.0(IE6)msxml3.dllMSXML2.XMLHttp.3.0
4.0msxml4.dllMSXML2.XMLHttp.4.0
5.0msxml5.dllMSXML2.XMLHttp.5.0
6.0msxml6.dllMSXML2.XMLHttp.6.0

建立XMLHttpRequest物件

在Mozilla,Chrome和Safaris等主流瀏覽器是內建物件,例如,

// 1. 透過 new XMLHttpRequest() 產生物件
// 2. 注意大小寫
var xmlHttp=new XMLHttpRequest();

在IE6之前是透過ActiveX控制項物件(由MSXML提供),例如,

var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");

IE7之後,同時支援原生XMLHttpRequest物件(預設值)和ActiveX控制項物件,例如,

var xmlHttp;
function createXMLHttpRequest(){
  if (window.XMLHttpRequest) //支援原生型
    xmlHttp = new XMLHttpRequest();
  else if (window.ActiveXObject) //IE6以下
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}

[KKBruce補充] 建立XMLHttpRequest物件注意事項

XMLHttpRequest物件屬性

JavaScript XMLHttpRequest物件屬性
名稱說明
readyState唯讀屬性,目前狀態
responseBody唯讀屬性,取得伺服器的回應,傳回非符號位元組的陣列
responseSteam唯讀屬性,取得伺服器的回應,傳回IStream資料流物件
responseText唯讀屬性,取得伺服器的回應,傳回字串
responseXML唯讀屬性,取得伺服器的回應,傳回XML DOM物件,可作DOM處理
status唯讀屬性,取得HTTP狀態碼
statusText唯讀屬性,取得HTTP狀態訊息字串
onreadystatechange存取屬性,執行readyState屬性改變的事件處理,設定處理的程序

readyState會回傳一數值,以表示目前處理的進度。

JavaScript readyState回傳狀態值
名稱說明
0uninitalized,未初始化
1loading,載入中
2loaded,載入完成
3interactive,可互動
4completed,完成

status會回傳狀態碼,以表示目前處理的結果。

流程 -
名稱說明
200OK,請求成功
202請求被接收,但處理未完成
400錯誤的請求
404Not Found,找不到網頁
500Internal Server Error,內部錯誤
...…其他網頁錯誤碼

[KKBruce補充] 這裡有一篇整理很棒的 HTTP 狀態碼,【HTTP 狀態碼,在 IIS 7.0 和 IIS 7.5】非常建議加入我的最愛中,在查詢錯誤的 HTTP 狀態碼時非常有用。例如,我們一般只知道 404 狀態碼是找不到,但 404 下還有整整 20 項細部的定義,這對於開發人員是不可多得的資訊,可以更快速清楚問題所在,而不是用經驗法則在處理。

XMLHttpRequest物件方法

JavaScript XMLHttpRequest物件方法
名稱說明
abort()取消目前HTTP請求
getAllResponseHeaders()取得全部HTTP標頭內容
getResponseHeader("指定Header名稱")取得指定HTTP標頭名稱的內容
open("GET|POST","URL",async,userid,pwd)開啟HTTP請求
send("參數")傳送HTTP請求到伺服器
setRequestHeader("自訂Header名稱",value)自訂HTTP標頭資料

XMLHttpRequest物件open方法參數
名稱說明
GET取得伺服器回應
POST傳送資料到伺服器
URL請求的XML文件或伺服端程式(ASP.NET, PHP, JSP...)
async布林值, 預設true,false表示需要等到伺服器回應後才繼續執行Javascript
userid, pwd伺服器需要id/password,才需要使用

send("參數")說明

參數表示要向伺服器發送的資料,參數格式為查詢字串的形式,例如,

var param = "name=Bruce&age=18";
在open中指定為GET,則參數作為查詢字串傳送,如果指定為POST,則作為HTTP的POST方法傳送,對於send()方法而言,此參數為必需, 如不發送任何資料,請傳送null即可。

// GET
xmlHttp.send(null);
// POST
xmlHttp.send(param);

setRequestHeader()說明

如果使用POST方法,在發送之前必須設定HTTP標頭,例如,

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// 加上UTF-8是最好
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");

AJAX下的GET與POST

function startRequest(){
    // 建立xmlHttp物件
 createXMLHttpRequest(); 
 // 向Demo.aspx,使用GET開啟請求
 xmlHttp.open("GET","Demo.aspx");
 // 狀態有改變時,就執行
 xmlHttp.onreadystatechange = function(){
     // 當狀態值為4(完成)與狀態碼為200(請求成功)
  if(xmlHttp.readyState == 4 && xmlHttp.status == 200)
      // Do something
   alert("服務器傳回訊息: " + xmlHttp.responseText);
 }
 // 傳送HTTP請求到伺服器
 // GET記得加上null
 xmlHttp.send(null);
}

IE會自動暫存非同步的結果,解決方法是每次非同步請求的URL不同,我們可以加上一個伺服器不需要的參數,讓每次的URL都不同。

// 加上一個時間戳計當亂數使用
var url = "Demo.aspx?timestamp=" & new Date().getTime();
xmlHttp.open("GET",url, true);

GET請求如果有參數,直接放入URL網址中,例如,

var queryString = "name=Bruce&age=18";
var url = "Demo.aspx?" + queryString + "&timestamp=" + new Date().getTime();

POST請求有參數,統一放在send()中來傳送,例如,

var queryString = "name=Bruce&age=18";
var url = "Demo.aspx?timestamp=" & new Date().getTime();
// url放在open()
xmlHttp.open("POST",url,true);
...
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
// queryString放在send()
xmlHttp.send(queryString);

POST方法中文會出現亂碼

在IE8與Firefox上測試已無此問題,但為了相容性還是建議加上解決方法。

這是因為XMLHttpRequest物件在處理傳回responseText/XML時是使用UTF-8編碼,建議使用解決方法2,對回傳的資料進行decodeURIComponent()解碼即可。

解決方法一

對要傳送的資料進行兩次encodeURI()編碼,以解決中文亂碼問題,例如,

...
xmlHttp.send(encodeURI(encodeURI(queryString)));

對傳回資料responseText/XML再進行一次decodeURI()解碼,例如,

responseDiv.innerHTML = decodeURI(xmlHttp.responseText);

解決方法二

setRequestHeader直接宣告(charset)使用UTF-8,例如,

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
xmlHttp.send(queryString);

對傳回資料responseText/XML進行decodeURIComponent()解碼,例如,

responseDiv.innerHTML = decodeURIComponent((xmlHttp.responseXML));

解決方法三

ASP,ASP.NET: 輸出之前Response.Charset = "big5 | gb2312",依取得的頁面charset設定。

onreadystatechange方法

AJAX的重點在狀態改變後的事件處理函式(handler),也可以說,這是唯一我們必須自訂(手動)設計程式碼的地方,換個方向說,我們在請求/回應,而伺服器回應資料後,我們要如何處理及顯示到網頁上(DOM)。

同時觸發非同步問題

如果我們必須同時觸發(呼叫)多個AJAX物件,因為我們通常都是把建立的AJAX物件的xmlHttp宣告為全域變數,所以可能會產生覆蓋的情況。

解決同時觸發非同步問題

就是將xmlHttp宣告為區域變數來處理,並在收到伺服器資料且處理完後,手動刪除。

function getData(){
  var xmlHttp="";
  createXMLHttpRequest();
  xmlHttp.onreadystatechange = function(){
    ...
    // 處理完立即刪除與釋放
    delete xmlHttp;
    xmlHttp = null;
  }
}

delete關鍵字

刪除物件的property。The delete operator deletes an object, an object's property, or an element at a specified index in an array.

JavaScript - XML DOM簡介

Asynchronous JavaScript And XML, AJAX

  1. HTML/XHTML與CSS:顯示UI與相關資料
  2. XML:伺服端非同步傳遞的資料
  3. XML DOM:當Browser非同步取得XML資料後,可進一步使用Javascript和XML DOM取出所需資訊
  4. XMLHttpRequest:Javascript透過XMLHttpRequest物件來建立非同步HTTP請求

Extensible Markup Language, XML

利用特殊的標示語言(Markup Language),建立XML文件(XML Document)。

XML Document

XML Document是由元素(Elements)組成。

標籤(Tags)

XML能自己定義標籤,如:<book>、<title>…。

元素(Elements)

元素是一個完整項目:<book code="K2010">Javascipt入門</book> 開始標籤、屬性、文字內容、結尾標籤。

屬性

屬性值需要使用引號(單、雙皆可)括起。

實體參考(Entity Reference)

XML本身有一些保留符號,如文件內需要使用這些符號,請使用實體參考。

JavaScript XML實體參考
實體參考代表符號
&lt;<
&gt;>
&amp;&
&apos;'
&quot;"

註解(Comment)

與HTML相同,例如,

<!-- 註解內容 -->

PCDATA

文字內容稱PCDATA,例如,

<book code="K2010">Javascipt入門</book>

PCDATA → Javascript入門。

CDATA

Character Data,表示XML在剖析文件時不用處理此區塊的內容,例如,

<script type="text/javascript">
<![CDATA[
    function a(){
    ...
    }
]]>
</script>
CDATA的註解
在HTML解析器(Browser)是不承認CDATA開始"<![CDATA["與結束"(]]>"標籤,也不認識HTML實體參考(Entity Reference), 可能導致會把問題顯示在Browser,並可能產生"跨網站腳本"問題,所以在HTML會建議註解CDATA。

範例一:JavaScript註解
<script type="text/javascript">
// <![CDATA[
  document.write("<");
// ]]>
</script>

範例二:CSS註解

<style type="text/css">
/*<![CDATA[*/
  body { background-image: url(marble.png) }     
/*]]>*/
</style>

Processing Instructions, PI

允許XML文件包含傳送給應用程式的指令,例如開頭宣告就是PI,PI如同註解,不屬於XML文件內容。

<?xml version="1.0" encoding="utf-8" ?>

Document Type Declarations, DTD

是XML文件的驗證機制,可以檢查XML標籤與文件架構是否正確。

<!DOCTYPE book [
<!ELEMENT book (code, title, author, price)>
<!ELEMENT code (#PCDATA)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (zh-tw_name, zh-en_name)>
<!ELEMENT zh-tw_name (#PCDATA)>
<!ELEMENT zh-en (#PCDATA)>
<!ELEMENT price (#PCDATA)>
]>

一行一行看,不會很難。

  • <!DOCTYPE book [:book之下包含以下元素
  • <!ELEMENT book (code, title, author, price)>:元素book之下包含其他子元素
  • <!ELEMENT code (#PCDATA)>:code元素含文字內容

XHTML

Well-Format(良好格式)的HTML。什麼是Well-Format?

  1. 標籤、屬性都是小寫
  2. 一定要<html>為根元素
  3. 標籤必須有結尾標籤,空元素需以 />結束標籤
  4. 巢狀標籤不能重疉
  5. 屬性必須有值
  6. 屬性值需使用引號
  7. Script需使用CDATA

第三點,標籤必須有結尾標籤,例如,

<-- 有頭有尾 -->
<b>粗體字</b>
<-- 空元素需以 />結束標籤 -->
<input id="name" type="text" />
<br />

第五點,屬性必須有值,例如,

<-- HTML -->
<input type=radio checked>

<-- XHTML -->
<input type="radio" checked="checked" />

Document Object Model, DOM

Document Object Model,XML DOM可以將XML文件視為一棵樹狀結構的節點, XML DOM就是W3C DOM針對XML文件所提供應用程式的標準程式介面, 可以透過API來存取各節點物件,將一份結構化文件(XML Document)轉換成一棵節點(Nodes)樹, 提供節點的屬性、方法來存取節點內容,或新增、修改、刪除節點內容。例如,

<book>
  <code>K2010</code>
  <title>Javascript入門</title>
  <author>
   <zh-tw_name>金剛</zh-tw_name>
   <zh-en_name>Bruce</zh-en_name>
  </author>
</book>

經過DOM剖析器之後,如圖,

DOM樹
圖一:DOM樹


MSXML

在Javascript程式可使用IE內建的MSXML元件來建立XML DOM物件,建立物件使用ProgID字串,但MSXML物件只能在IE使用,要注意相容性問題。

KKBruce提醒:實務上已經很少人使用MSXML來撰寫程式碼,以下只是簡單介紹,大多都已經使用標準AJAX方式(XMLHttpRequest物件)來處理。

JavaScript MSXML DOM版本
版本DLL名稱ProgID
2.0msxml.dllMicrosoft.XMLDOM或MSXML.DOMDocument
2.6msxml2.dllMSXML2.DOMDocument
3.0msxml3.dllMSXML2.DOMDocument.3.0
4.0msxml4.dllMSXML2.DOMDocument.4.0
5.0msxml5.dllMSXML2.DOMDocument.5.0
6.0msxml6.dllMSXML2.DOMDocument.6.0

範例程式一(IE Only):

<table border="1">
<script type="text/javascript" >
//<![CDATA[
  // 建立XML DOM物件, 使用ProgID來建立
  var xmlDom = new ActiveXObject("MSXML2.DOMDocument.6.0");
  //async=false,可以保證XML文件能完全載入
  xmlDom.async = "false";
  // 載入XML文件
  xmlDom.load("Books.xml");
  // 建立文件元素子節點樹
  var objNode = xmlDom.documentElement.childNodes;
  // 顯示所有XML節點
  for ( i=0; i < objNode.length; i++ ) {
      document.write("<tr><td>");
      document.write(objNode.item(i).nodeName + "</td><td>");
      document.write(objNode.item(i).text + "</td></tr>");
  }
//]]>
</script>
</table>

範例程式二(IE Only):

>// 字串變數
var strXML = "<book>";
strXML = strXML + "<code>K2012</code>";
...
//載入字串變數
xmlDom.load(strXML);

先建立XMLDOM物件,然後由Books.xml當成資料來源,由XMLDOM物件讀入轉換成節點樹,再將節點讀取顯示出來。

[補充]使用MSXML2.DOMDocument.6.0與MSXML2.DOMDocument.3.0就好。