物件導向語言需要具備三種基本特徵:繼承(Inheritance)、多態 (Polymorphism) 、封裝(Encapsulation and Aggregation)。和Java 、C++ 等語言一樣,ECMAScript 被認為是物件導向的,因為它同時支援這些特徵。var obj = new Object();
該語句建立了一個對象,當建構函式沒有參數時,可以省略後面的括弧。我們通常是通過對象的引用來進行對象操作,當一個對象的引用為null時, ECMAScript將啟動記憶體回收程式刪除該對象,釋放記憶體。當一個對象存在多個引用時,需要將所有的引用設定為null來釋放該對象所佔用的空間。
1 早期繫結和動態綁定
早期繫結是指在對象被執行個體化之前對象的屬性和方法已經被定義,使得編譯器或解譯器能夠提前編譯機器碼。Java和VB支援這種特性,但ECMAScript 並非強型別,不支援早期繫結。
動態綁定是指編譯器或解譯器在運行時才知道該對象的具體類型,之前並不會檢查,只會判斷這些屬性和方法是否得到該對象的支援。ECMAScript對所有的變數使用動態綁定。
2 物件類型
2.1 原始對象
ECMA-262 定義不受主機環境影響的ECMAScript支援的任意對象為原始對象,簡而言之,原始對象就是ECMA-262定義的參考型別,比較常用的有 Object、Function、String、Boolean、Number、Array、Date。
Array
Array 有許多常用的方法,很多和Java類似,在此不作介紹。需要注意的是,使用push()和pop()兩個方法,可以將Array看作一個棧,遵循後進先出規則(LIFO)。使用shift()和push()兩個方法,Array對象即可看作是一個隊列,遵循後進後出的規則(LILO)。splice()方法非常有用,它輕鬆的實現了鏈表的一些準系統,如刪除資料項目、插入資料、替換資料。
Date
ECMAScript 中的Date類是基於Java早期版本中的java.util.Date類的,它可以精確表示1970年1月1日(格林威治時間)前後285616年的任意時間。Date是少有的幾個重寫了toString()和valueOf()方法,並且兩個方法並不相同的類。valueOf()通常表示精確到毫秒級的時間,toString()通常返回個人化的時間表示方法,例如相同的時間在不同瀏覽器下顯示的效果可能不一樣。
2.2 Build-in對象
Build-in 對象除了具備原始對象的基本特性外,它在 ECMAScript 程式開始運行時就可以使用,因此可以說任何 Build-in 對象就是原始對象。目前 ECMA-262 只定義了兩個 Build-in 對象: Global 和 Math 。
Global
在ECMAScript 裡,沒有函數可以單獨存在,所有的函數都必須是某個對象的方法。如isNan()、isFinite()、parseInt()、parseFloat ()、encodeURI()、encodeURIComponent()、decodeURI()、decodeURIComponent() 等均是Global對象的方法,除此之外,Global對象還包括一些屬性。
Math
Math中有許多屬性和方法可以用於數學計算,和Java中的比較類似。
2.3 Host 對象
除了原始對象和Build-in對象外,其他對象均是Host對象。所有的BOM和DOM對象均被認為是Host對象。
3 類成員的訪問方式
在物件導向語言程式設計中,常見的類成員訪問方式有public、protected和private。在ECMAScript中,只有public一種訪問方式,對象中所有的屬性和方法都是可見的,因此,在程式設計時需要額外注意系統安全性的問題。在沒有合理的程式規範之前,程式員在編寫 ECMAScript代碼時,通常使用一定規範的命名方式來說明該屬性或方法是私人的(僅僅是說明而已,實際上還是公有的),例如在名稱前後加上底線,或只在名稱前加底線。另外,在ECMAScript中沒有static方法。
4 this關鍵字
this關鍵字是ECMAScript中一個非常重要的概念,通常在對象的方法中使用。
function whatFruit() {
alert(this.color);
}
var table1 = new Object;
table1.fruit = "apple";
table1.whatFruit = whatFruit;
var table2 = new Object;
table2.fruit = "pear";
table2.whatFruit = whatFruit;
table1.whatFruit(); //輸出apple
table2.whatFruit(); //輸出pear
當一個變數前面沒有對象或this來引用時,ECMAScript認為該變數是一個本地的或全域變數,於是在本地和全域中去搜尋該變數,如果最後仍沒有找到,將會在alert中輸出null。
5 自訂類和對象
5.1 Factory 方法
在ECMAScript中建立Factory 方法,返回一個特定類型的對象,以此實現代碼的簡潔適用。
function createFruit() {
var tempFruit = new Object;
tempFruit.name = "apple";
tempFruit.number = 5;
tempFruit.showName = function() {
alert(this.name);
};
return tempFruit;
}
var Fruit1 = creatFruit();
var Fruit2 = creatFruit();
在createFruit()中可以加入形參來傳入參數的值。隨著ECMAScript不斷被正常化,這種建立對象的方法已不再流行,一部分原因是文法上的,一部分原因是功能上的,如每個對象的執行個體都擁有屬於自己的showName方法,給記憶體管理帶來一定的開銷。
5.2 建構函式
選擇一個類名,第一個字母大寫,該類名即是建構函式的名稱。建立一個建構函式和Factory 方法比較類似,不同的是需要使用關鍵字new來建立對象的引用。使用建構函式的方式來建立對象和使用Factory 方法有著相同的弊端。
function Fruit(name, number) {
this.name = name;
this.number = number;
this.showName = function() {
alert(this.name);
};
}
var Fruit1 = new Fruit("apple", 5);
var Fruit2 = new Fruit("pear", 3);
5.3 使用 Prototype
使用prototype屬性可以用來建立新的對象,首先需要一個空的建構函式建立類的名稱,然後所有的屬性和方法都直接分配到prototype屬性中。
function Fruit() {
}
Fruit.prototype.name = "apple";
Fruit.prototype.number = 5;
Fruit.prototype.showName = function() {
alert(this.name);
};
var fruit1 = new Fruit();
var fruit2 = new Fruit();
但是,這樣同樣存在一些缺點。首先,建構函式中沒有參數,給初始化帶來一些麻煩,其次,當一個屬性指向的是一個對象而非方法時,該對象會被所有的執行個體所共用,任何一點改動都會影響到其他對象引用的使用。
5.4 混合使用Factory 方法和Prototype
這個概念很簡單:使用建構函式定義所有除方法外的屬性,使用 prototype 定義對象的方法。這樣每個方法只會被建立一次,每個對象都能擁有自己對象執行個體的屬性。
function Fruit(name, number) {
this.name = name;
this.number = number;
this.owner = new Array("Jerry", "Terry");
}
Fruit.prototype.showName = function() {
alert(this.name);
};
var Fruit1 = new Fruit("apple", 5);
var Fruit2 = new Fruit("pear", 3);
5.5 動態 prototype
簡單來說,這種方法就是使用了一個標識符來判斷 prototype 是否已經被指向某個方法,從而保證這些方法只會被建立並指向一次。
5.6 混合Factory 方法
這種方法和經典的Factory 方法及建構函式方法在對象方法記憶體管理上存在同樣的問題,一般不建議使用該方法,除了某些特殊情況(XML in JavaScript中有這樣的例子)。
6 修改對象
使用prototype對象可以對對象進行修改。除了使用者自訂的對象外,ECMAScript原始對象也有prototype屬性。直接使用 prototype可以給對象建立新的方法。
Number.prototype.toHexString = function() {
return this.toString(16);
};
var iNum = 10;
alert(iNum.toHexString()); //輸出A
另外,使用prototype可以輕鬆修改已有的方法,讓方法名指向新的方法。需要注意的是,指向新的方法後,原有的方法不再被任何對象使用,將會被記憶體回收行程銷毀,使得原有方法不再存在。比較安全的解決辦法是,建立一個新的引用來儲存原有的方法,然後再將原方法覆蓋。
比較特殊的是,ECMAScript中建立對象,在對象引用被建立後,可以給對象加入新的方法,並且可以立即在對象的引用中使用。這是ECMAScript的一個特性,但不推薦這樣使用,以免帶來不必要的麻煩,例如閱讀理解、文檔資料等。