WEB入門之十一 JS面向對象
學習內容

JavaScript類的定義模式
JavaScript繼承的實現
JavaScript抽象類
JavaScript解析XML
能力目標
深入了解JavaScript類的定義模式
理解JavaScript繼承
理解JavaScript抽象類和虛函數
熟練使用JavaScript進行XML解析
本章簡介
上一章學習了JavaScript高級編程,包括匿名函數、內部函數、回調函數以及使用JavaScript解析JSON格式的數據,另外還學習了基礎的JavaScript面向對象編程,包括類的定義和對象的創建。本章繼續深入學習JavaScript面向對象編程,會涉及到類的多種定義模式、繼承、抽象類等高級內容。
核心技能部分
2.1?類的定義模式
JavaScript并不像C#、Java一樣支持真正的類。但是在上一章,可以通過函數來模擬類的定義,實際上JavaScript支持多種類的定義模式,例如構造函數模式、原型模式等。
2.1.1?構造函數模式
構造函數模式是最常用的一種類的定義模式,上一章的示例1.6就是通過構造函數模式來定義類的。下面再使用構造函數模式定義一個Person類。參考代碼如下所示。
示例2.1
function School(n,a)
{
this.name=n;
this.address=a;
this.teach=function(){
alert(n+"正在上課...");
}
}
var zy=new School("鄭州中學","瑞達路");
zy.teach();
上述代碼使用一個函數模擬定義了一個School類
從運行結果來看,School函數執行了,同時zy獲得了一個對象的引用。事實上,當new一個函數時,這個函數就表示構造函數,該函數里面的代碼可以看做是為了初始化一個對象而工作。
構造函數通常沒有返回值,并且在函數內部使用this關鍵字表示新創建的對象。構造函數首字母通常大寫,這也是為了符合OOP的編碼慣例,也為了跟普通函數進行區別。
在JavaScript中通過構造函數創建對象的過程總結如下:
1.瀏覽器解釋器遇到new關鍵字時創建一個空對象
2.執行構造函數,并將this指針指向新建的對象
3.初始化屬性和方法
4.函數執行完畢就返回初始化后的對象
2.1.2?原型模式
prototype即原型,它本身就是一個對象。每個函數(類)都有一個prototype屬性(子對象),它表示類的成員集合。當使用new創建對象時,prototype對象的成員都會成為新建對象的成員。
我們首先通過一個例子來演示什么是prototype以及它的用法,參考代碼如下所示。
示例2.2
function School(){
}
School.prototype.name="鄭州中學";
School.prototype.address="瑞達路";
School.prototype.teach=function(){
alert(this.name+"正在上課...");
}
var zy=new School();
zy.teach();
上述代碼首先定義了一個空類,然后使用prototype定義了該類的屬性和方法,運行效果跟示例2.1一樣。上述代碼有一個問題就是每定義一個屬性或方法就需要寫一次School.prototype,我們可以做如下改進。
示例2.3
function School(){
}
School.prototype={
name:"鄭州中學",
address:"瑞達路",
teach:function(){
alert(this.name+"正在上課...");
}
}
var zy=new School();
zy.teach();
上述代碼通過大括號把成員集中添加到了prototype對象上,寫法更加簡潔清晰。prototype對象專用于設計類的成員,是JavaScript實現面向對象編程的重要手段。在JavaScript中通過原型創建對象的過程總結如下:
1.瀏覽器解釋器遇到new關鍵字時創建一個空對象
2.將this指針指向新建的對象
3.把prototype對象的所有成員賦給新建的對象
4.返回初始化后的對象
2.1.3?兩種模式的不同之處
構造函數模式和原型模式都能定義類,但是這兩種模式之間有著本質的區別。下面我們通過例子進行對比。
示例2.4
function School(n,a)
{
this.name=n;
this.address=a;
this.teach=function(){
document.write(n+"正在上課...
");
}
}
var s1=new School("鄭州中學","瑞達路");
s1.teach();
var s2=new School("鄭州大學","科學大道");
s2.teach();
示例2.4使用的是前面的例子,通過構造函數定義了一個School類,并創建了該類的兩個對象s1和s2。
在JavaScript中,函數就是類,所以School類中的teach函數其實就是一個對象。這會產生這樣一個問題:每次創建一個School類的對象就會創建一個teach對象,而每個teach所實現的功能是一模一樣的,沒必要多次創建,這是通過構造函數定義類的一個缺點。
下面我們使用原型也定義一個和上面一樣的School類,同樣也創建該類的兩個對象s1和s2,參考代碼如下所示。
示例2.5
function School(){
}
School.prototype.name="鄭州中學";
School.prototype.address="瑞達路";
School.prototype.teach=function(){
document.write(this.name+"正在上課...
");
}
var s1=new School("鄭州中學","瑞達路");
s1.teach();
var s2=new School("鄭州大學","科學大道");
s2.teach();
直接看運行效果,如圖2.1.3所示。上述代碼雖然使用new創建了兩個對象,但是通過運行結果會發現,這兩個對象中的成員的值一樣,第2個對象采用的仍然是第1個對象的值。原型中的所有成員是被類的所有對象共享的,類似于Java類中的靜態成員,即無法像構造函數那樣創建不同的對象,這是通過原型定義類的一個缺點。
2.1.4?兩種模式組合使用
由于構造函數模式和原型模式各有優缺點,所以在實際開發中通常組合使用兩種模式來定義類。構造函數負責定義類中的屬性,原型負責定義類中的方法,這樣創建的每個對象就會有不同的屬性值,同時又不會每次都創建類中的函數對象,節省了內存。
下面我們組合使用兩種模式定義School類,參考代碼如下所示。
示例2.6
function School(n,a){
this.name=n;
this.address=a;
}
School.prototype.teach=function(){
document.write(this.name+"正在上課...
");
}
var s1=new School("鄭州中學","瑞達路");
s1.teach();
var s2=new School("鄭州大學","科學大道");
s2.teach();
上述代碼通過帶參的構造函數給屬性傳值,通過prototype定義方法。這種組合模式是ECMAScript中使用最廣泛、認同度最高的一種定義類的方式。
2.2?繼承
JavaScript沒有專門的機制來實現繼承。ECMAScript中提出了原型鏈的概念,并將原型鏈作為實現繼承的主要方式,其基本思想是利用原型讓一個自定義類繼承另一個類的屬性和方法。
示例2.7
//定義父類Person
function Person(n,a,p){
this.name=n;
this.age=a;
this.phone=p;
}
Person.prototype.sayHello=function(){
alert(this.name+"向你問好!");
}
//定義子類Student
function Student(s){
this.score=s;
}
//子類Student繼承父類Person
Student.prototype=new Person();
Student.prototype.sayHello=function(){
alert("學生"+this.name+"向你問好!");
}
Student.prototype.getDetials=function(){
alert(this.name+"的成績是"+this.score);
}
//創建子類對象
var s=new Student();
s.name="張無忌";
s.age="28";
s.phone="18638643721";
s.score=100;
s.sayHello();
s.getDetials();
上述代碼定義了兩個對象,分別是父類Person和子類Student。子類Student通過Student.prototype=new Person(); 繼承了父類Person,這就是原型繼承。另外,子類Student重寫了父類Person中的sayHello方法,還定義了自己獨有的屬性score和方法getDetials。
需要注意的是:子類重寫父類方法的代碼以及定義自身獨有方法的代碼必須出現在原型繼承Student.prototype=new Person(); 之后。
JavaScript也可以像Java一樣,在定義子類時調用父類的構造方法為屬性賦值。下面我們把示例2.7中的子類Student修改為帶參數的構造方法,參考代碼如下所示。
示例2.8
//定義父類Person
function Person(n,a,p){
this.name=n;
this.age=a;
this.phone=p;
}
Person.prototype.sayHello=function(){
alert(this.name+"向你問好!");
}
//定義子類Student
function Student(n,a,p,s){
Person.call(this,n,a,p);
this.score=s;
}
//子類Student繼承父類Person
Student.prototype=new Person();
Student.prototype.sayHello=function(){
alert("學生"+this.name+"向你問好!");
}
Student.prototype.getDetials=function(){
alert(this.name+"的成績是"+this.score);
}
//創建子類對象
var s=new Student("張無忌",28,"18638643721",100);
s.sayHello();
s.getDetials();
上述代碼使用帶參數的構造函數定義了一個子類Student,在子類的構造函數中使用Person.call(this,n,a,p);調用了父類的構造函數。call方法用來綁定到某個函數上進行自身調用,第一個參數this表示函數本身,后面的都是函數的參數。
2.3?解析XML
XML是指可擴展標記語言(Extensible ?Markup ?Language),類似于HTML。它是W3C的推薦標準,其設計宗旨是存儲和傳輸數據而非顯示數據。XML標簽沒有被預定義,需要開發人員自定義標簽。
XML的主要作用可以概括為以下6個:
(1)使數據從HTML分離。
如果需要在HTML文檔中顯示動態數據,則每次數據改變時將花費大量的時間來編輯HTML。通過XML,數據能夠存儲在獨立的XML文件中,從而專注地使用HTML進行布局和顯示,且修改底層數據時無須再次對HTML進行任何改變。通過幾行JavaScript即可讀取一個外部XML文件,然后更新HTML中的數據內容。
(2)簡化數據共享。
XML數據以純文本格式進行存儲,提供了一種獨立軟件和硬件的數據存儲方法,使創建不同應用程序共享的數據變得更加容易。
(3)簡化數據傳輸。
通過XML可以在不兼容的系統之間輕松交換數據。對于開發人員而言,在因特網上不兼容的系統之間交換數據特別耗費時間。可以通過各種不兼容的應用程序來讀取數據,使用XML交換數據可以降低這種復雜性。
(4)簡化平臺的變更。
升級到新系統(硬件或軟件平臺)相對費時的情況下,不兼容的數據在轉換大量的數據時經常丟失。XML數據以文本格式存儲,這使XML可以在不丟失數據的前提下更易于擴展或升級到新的操作系統、應用程序或瀏覽器。
(5)延伸了數據使用。
XML獨立于硬性、軟件以及應用程序之外,使數據更可用、有用。不同的應用程序都能夠在HTML頁面以及XML數據源中訪問。通過XML的數據不僅可以供各種閱讀設備(手持計算機、語音設備、新聞閱讀器等)使用,還可以供盲人等殘障人士使用。
2.3.1?節點和節點樹
根據DOM的規定,XML文檔中每個單元(元素、屬性、文本、注釋等)都是節點。例如:
(1)整個文檔是一個文檔節點。
(2)每個XML標簽是一個元素節點。
(3)包含在XML元素中的文本是文本節點。
(4)每個XML屬性是一個屬性節點。
(5)注釋屬于注釋節點。
XML DOM將XML文檔視為樹結構,這種樹結構被稱為節點樹。程序通過節點樹訪問所有節點、修改或刪除其內容以及創建新元素。節點樹展示了節點的集合以及它們之間的關系。節點樹從根節點開始,在樹的最低層級向文本節點長出“枝條”。
下面我們編寫一個描述和存儲圖書信息的book.xml,并給出節點數的圖形,參考代碼如下所示。
示例2.9
將示例2.9中的books.xml使用節點樹表示
下面我們分析一下book.xml文件:
(1)根節點是
(2)根節點
(3)第一個
(4)元素節點的文本存儲在文本節點中。
(5) 屬性節點與子元素屬于同一級別的節點。例如,
2.3.2?DOM編程接口
由于客戶端無法識別并讀取Java對象數據,所以,在Web開發中,服務器端經常返回的是XML數據。因此,客戶端需要使用JavaScript解析XML數據。
XML DOM對象中封裝了常用的操作XML文檔的屬性和方法。常用的XML DOM對象如下:
(1)XML DOM Attr對象:表示Element對象的屬性。
(2)XML DOM Comment對象:表示文檔中注釋節點的內容。
(3)XML DOM Document對象:表示整個XML文檔。
(4)XML DOM Element對象:表示XML文檔中的元素。
(5)XML DOM Node對象:表示文檔樹中的一個節點。
(6)XML DOM Text對象:表示元素或屬性的文本內容。
(7) XML DOM XMLHttpRequest對象:提供對HTTP協議的訪問,包括發出POST、HEAD以及普通GET請求的能力。XMLHttpRequest可以同步或異步地返回Web服務器的響應,并且通過文本或者一個DOM文檔的形式返回內容。此對象并不限于和XML文檔一起使用,可以接收任何形式的文本文檔。
瀏覽器都內建了用于讀取和操作XML文件的XML解析器。解析器將XML讀入內存,并轉換為可以被JavaScript訪問的XML DOM對象。
XML數據可以通過XML文檔保存在磁盤介質上,或者通過XML字符串在內存中創建。XMLDocument對象將XML文檔和XML字符串加載到內存,然后通過JavaScript實現DOM解析。如果XML文檔需要在服務端解析,還可以使用C#、Java等編程語言通過DOM API進行解析。
使用JavaScript實現DOM時,不同瀏覽器的加載方式有所不同。
1.使用load ( )方法加載XML文檔
示例2.10
//IE瀏覽器
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.load("book.xml");
示例2.11
//Firefox、Mozilla等瀏覽器
xmlDoc = document.implementation.createDocument("", "", null);
xmlDoc.async = false;
xmlDoc.load("book.xml");
在示例2.10和示例2.11中,第一行代碼用于在瀏覽器中創建空的XML Document對象;第二行關閉異步加載,可以確保在文檔完整加載之前,解析器不會繼續執行腳本;第三行通知解析器加載名為“books.xml”的文檔。在實際開發中可以通過異常處理來編寫通用的瀏覽器加載方法。示例2.12通過異常處理模塊編寫跨瀏覽器的XML文檔加載代碼。
示例2.12
try {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
} catch (e) {
try {
xmlDoc = document.implementation.createDocument("", "", null);
} catch (e) {
alert(e.message);
}
}
try {
xmlDoc.async = false;
xmlDoc.load("book.xml");
document.write("XML文檔已經加載完畢,可以進行解析了。
");
} catch (e) {
alert(e.message);
}
2.使用loadXml()方法加載XML文本
如果服務器端返回的不是xml文件,而是xml格式的文本信息,那么在IE瀏覽器中使用XML Document對象的loadXml()方法加載XML文本;在Firefox等瀏覽器中使用DOM解析器對象DOMParser的parseFromString()方法加載XML文本。
下面我們通過一個例子來演示加載XML文本,參考代碼如下所示。
示例2.13
text = "
text = text + "
text = text + "
text = text + "
text = text + "
text = text + "";
text = text + "";
try
{
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
} catch (e) {
try
{
parser = new DOMParser();
xmlDoc = parser.parseFromString(text, "text/xml");
} catch (e) {
alert(e.message);
}
}
try {
xmlDoc.loadXML(text);
document.write("XML字符串已經加載完畢,可以進行解析了。");
} catch (e) {
alert(e.message);
}
節點操作通過XML DOM對象的屬性和方法實現。對象多達幾十個,并且每個對象都有各自的屬性和方法,常用對象的屬性和方法見
XML DOM對象常用屬性
屬性
說明
nodeName
獲取節點名稱
nodeValue
獲取節點的值
parentNode
獲取節點的父節點
childNodes
獲取節點的所有子節點集合
attributes
獲取當前節點所有的屬性節點
documentElement
獲取文檔的根節點
表2-1-2 ?XML DOM對象常用方法
方法
說明
getElementsByTagName(name)
獲取帶有指定標簽名(name)的所有元素
CreateElement(name)
創建指定標簽名的元素節點
appendChild(node)
向調用節點末尾插入子節點node
removeChild(node)
從調用節點中刪除子節點node
下面是一個通過XML DOM對象的屬性和方法來解析book.xml的例子,參考代碼如下所示。
示例2.14
try {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
} catch (e) {
try {
xmlDoc = document.implementation.createDocument("", "", null);
} catch (e) {
alert(e.message);
}
}
try {
xmlDoc.async = false;
xmlDoc.load("book.xml");
} catch (e) {
alert(e.message);
}
//使用屬性和方法從book.xml中的
var txt=xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue;
alert(txt);
對示例2.14的說明如下:
(1)xmlDoc:由解析器創建的XML Document對象,創建方式見示例2.13。
(2)getElementsByTagName("title")[0]:獲取第一個
(3)childNodes[0]:獲取
(4)nodeValue:獲取節點的值(文本自身)。
訪問節點包括遍歷節點、定位節點、獲取節點的詳細信息等操作,這些操作都通過XML DOM的屬性和方法實現。通常,訪問節點需要使用對象XML Node List和XML Node。前者表示一個節點列表(集合),后者表示一個節點。每個節點都具有nodeName、nodeValue和nodeType屬性,分別用于獲得節點名稱、節點值和節點的類型。元素節點還可以通過attributes屬性返回屬性節點的列表。
??nodeName。其特點是:nodeName是只讀的;元素節點的nodeName與標簽名相同;屬性節點的nodeName是屬性的名稱。
??nodeType。nodeType屬性規定節點的類型,是只讀的。nodeType常用的值包括:“1”
表示節點是元素節點:“2”表示節點是屬性節點;“3”表示節點是文本節點;“8”
表示節點是注釋節點。
??nodeValue。其特點是:元素節點的nodeValue是不可用的;文本節點的nodeValue是文本自身;屬性節點的nodeValue是屬性的值。
在DOM層次中還可以通過以下3種方法查找和定位節點:
(1)使用getElementsByTagName()方法。
(2)循環(遍歷)節點樹。
(3)通過節點的層次關系在節點樹中導航,見表2-1-3。
表2-1-3 ?節點層次關系
屬性
功能
parentNode
獲取父節點
childNodes
獲取子節點集合
firstChild
獲取第一個子節點
lastChild
獲取最后一個子節點
nextSibling
獲取同級別中后一個節點
previousSibling
獲取同級別中前一個節點
下面我們通過一個例子來演示如何使用節點層次關系來解析book.xml文件,參考代碼如下所示。
示例2.15
try {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
} catch (e) {
try {
xmlDoc = document.implementation.createDocument("", "", null);
} catch (e) {
alert(e.message);
}
}
try {
xmlDoc.async = false;
xmlDoc.load("book.xml");
} catch (e) {
alert(e.message);
}
var nlist = xmlDoc.getElementsByTagName("book")[0].childNodes;
document.write("
book節點包含子元素的個數: " + nlist.length);
document.write("
");
for ( var i = 0; i < nlist.length; i++) {
var node = nlist[i];
alert(node.nodeType);
document.write("子元素的名字:" + node.nodeName + ",子元素的值:"
+ node.childNodes[0].nodeValue);
var att = node.attributes;//獲取該元素的全部屬性節點
for ( var j = 0; j < att.length; j++) {
document.writeln("
子元素" + node.nodeName + "有屬性"
+ att[i].nodeName + "=" + att[i].nodeValue);
}
document.write("
");
}
上述代碼首先使用getElementsByTagName("book")[0].childNodes獲得了book下的所有子節點,然后使用for循環遍歷這個子節點集合,在循環過程中輸出每個節點的名字和值,有屬性的同時使用for循環輸出屬性節點的名字和值。
本章總結
本章在上一章的基礎上進一步講解了JavaScript面向對象編程,包括類的定義模式和繼承等,這一部分內容著重強調理解,大家可以結合Java面向對象編程思想幫助自己理解。JavaScript解析XML在實際開發中應用頻繁,大家必須要掌握熟練。
任務實訓部分
1:自定義類
訓練技能點
??構造函數模式
??原型模式
需求說明
使用構造函數模式和原型模式組合定義一個員工類,包括員工姓名、職位和工資,以及
開會的方法。創建兩個員工對象進行測試。
2:JavaScript繼承
訓練技能點
通過prototype實現繼承
需求說明
首先定義一個寵物類(Pet)作為父類,屬性和方法自定義;然后定義一個Dog類和Cat類都繼承Pet類;最后創建一個Dog對象和Cat對象進行測試。
3:解析XML文件
訓練技能點
??JavaScript解析XML文件
需求說明
創建一個XML文件存儲3個學生信息(姓名、性別、成績);使用JavaScript解析該文件并逐行輸出這3個學生的所有信息。
實現步驟
(1)定義一個XML文件存儲3個學生信息
(2)使用JavaScript解析XML文件并輸出,參考代碼如下所示。
try {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
} catch (e) {
try {
xmlDoc = document.implementation.createDocument("", "", null);
} catch (e) {
alert(e.message);
}
}
try {
xmlDoc.async = false;
xmlDoc.load("students.xml");
} catch (e) {
alert(e.message);
}
var stus=xmlDoc.getElementsByTagName("student");
for(var j=0;j { var stu=stus[j].childNodes; for(var k=0;k { document.write(stu[k].firstChild.nodeValue+" "); } document.write(" } 4:解析XML文本 訓練技能點 ??JavaScript解析XML文本 需求說明 通過字符串存儲XML格式的數據:3個員工信息(姓名、職務、工資),然后使用JavaScript解析該XML文本并逐行輸出這3個員工的所有信息。 鞏固練習 一、選擇題 1. 以下關于JavaScript構造函數說法正確的是()。 A.?JavaScript類不存在構造函數 B.?JavaScript類的構造函數必須帶有參數 C.?構造函數首字符必須大寫 D.?構造函數中的this表示新建的對象 2.?以下關于JavaScript原型的說法錯誤的是()。 A.?原型也是一個對象 B.?每個自定義類都默認擁有原型屬性 C.?原型擁有類的成員集合 D.?原型里的this表示原型自身 3.?以下關于JavaScript繼承說法錯誤的是()。 A.?JavaScript沒有繼承機制 B.?JavaScript可以借助原型實現繼承 C.?JavaScript子類無法重寫父類方法 D.?JavaScript子類中可以調用父類的構造函數 4.?下列關于XML說法錯誤的是()。 A.?XML和HTML的作用一樣,只不過標記需要自定義而已 B.?XML主要用來存儲、傳輸數據,能夠跨平臺 C.?不同瀏覽器加載XML文件的方式不同 二、上機練習 web前端 XML 面向對象編程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
");