JavaScript對象與傳統的物件導向中的對象幾乎沒有相似之處,傳統的物件導向語言中,建立一個對象必須先有對象的模板:類,類中定義了對象的屬性和操作這些屬性的方法。通過執行個體化來構築一個對象,然後使用對象間的協作來完成一項功能,通過功能的集合來完成整個工程。而Javascript中是沒有類的概念的,藉助JavaScript的動態性,我們完全可以建立一個空的對象(而不是類),通過像對象動態添加屬性來完善對象的功能。
JSON是JavaScript中對象的字面量,是對象的表示方法,通過使用JSON,可以減少中間變數,使代碼的結構更加清晰,也更加直觀。使用JSON,可以動態構建對象,而不必通過類來進行執行個體化,大大的提高了編碼的效率。
Javascript對象
JavaScript對象其實就是屬性的集合,這裡的集合與數學上的集合是等價的,即具有確定性,無序性和互異性,也就是說,給定一個JavaScript對象,我們可以明確的知道一個屬性是不是這個對象的屬性,對象中的屬性是無序的,並且是各不相同的(如果有同名的,則後聲明的覆蓋先聲明的)。
一般來說,我們聲明對象的時候對象往往只是一個空的集合,不包含任何的屬性,通過不斷的添加屬性,使得該對象成為一個有完整功能的對象,而不用通過建立一個類,然後執行個體化該類這種模式,這樣我們的代碼具有更高的靈活性,我們可以任意的增刪對象的屬性。
如果讀者有python或其他類似的動態語言的經驗,就可以更好的理解JavaScript的對象,JavaScript對象的本身就是一個字典(dictionary),或者Java語言中的Map,或者稱為關聯陣列,即通過鍵來關聯一個對象,這個對象本身又可以是一個對象,根據此定義,我們可以知道JavaScript對象可以表示任意複雜的資料結構。
對象的屬性
屬性是由索引值對組成的,即屬性的名字和屬性的值。屬性的名字是一個字串,而值可以為任意的JavaScript對象(JavaScript中的一切皆對象,包括函數)。比如,聲明一個對象: 複製代碼 代碼如下://聲明一個對象
var jack = new Object();
jack.name = "jack";
jack.age = 26;
jack.birthday = new Date(1984, 4, 5);
//聲明另一個對象
var address = new Object();
address.street = "Huang Quan Road";
address.xno = "135";
//將addr屬性賦值為對象address
jack.addr = address;
這種聲明對象的方式與傳統的OO語言是截然不同的,它給了我們極大的靈活性來定製一個對象的行為。
對象屬性的讀取方式是通過點操作符(.)來進行的,比如上例中jack對象的addr屬性,可以通過下列方式取得: 複製代碼 代碼如下:var ja = jack.addr;
ja = jack[addr];
後者是為了避免這種情況,設想對象有一個屬性本身包含一個點(.),這在JavaScript中是合法的,比如說名字為foo.bar,當使用jack.foo.bar的時候,解譯器會誤以為foo屬性下有一個bar的欄位,因此可以使用jack[foo.bar]來進行訪問。通常來說,我們在開發通用的工具包時,應該對使用者可能的輸入不做任何假設,通過[屬性名稱]這種形式則總是可以保證正確性的。
屬性與變數
在第二章,我們講解了變數的概念,在本章中,讀者可能已經注意到,這二者的行為非常相似,事實上,對象的屬性和我們之前所說的變數其實是一回事。
JavaScript引擎在初始化時,會構建一個全域對象,在用戶端環境中,這個全域對象即為window。如果在其他的JavaScript環境中需要引用這個全域對象,只需要在頂級範圍(即所有函式宣告之外的範圍)中聲明:
複製代碼 代碼如下:var global = this;
我們在頂級範圍中聲明的變數將作為全域對象的屬性被儲存,從這一點上來看,變數其實就是屬性。比如,在用戶端,經常會出現這樣的代碼:
複製代碼 代碼如下:var v = "global";
var array = ["hello", "world"];
function func(id){
var element = document.getElementById(id);
//對elemen做一些操作
}
事實上相當於: 複製代碼 代碼如下:window.v = "global";
window.array = ["hello", "world"];
window.func = function(id){
var element = document.getElementById(id);
//對elemen做一些操作
}
原型對象
原型(prototype),是JavaScript特有的一個概念,通過使用原型,JavaScript可以建立其傳統OO語言中的繼承,從而體現對象的層次關係。JavaScript本身是基於原型的,每個對象都有一個prototype的屬性來,這個prototype本身也是一個對象,因此它本身也可以有自己的原型,這樣就構成了一個鏈結構。
訪問一個屬性的時候,解析器需要從下向上的遍曆這個鏈結構,直到遇到該屬性,則返回屬性對應的值,或者遇到原型為null的對象(JavaScript的基底物件Object的prototype屬性即為null),如果此對象仍沒有該屬性,則返回undefined.
下面我們看一個具體的例子: 複製代碼 代碼如下://聲明一個對象base
function Base(name){
this.name = name;
this.getName = function(){
return this.name;
}
}
//聲明一個對象child
function Child(id){
this.id = id;
this.getId = function(){
return this.id;
}
}
//將child的原型指向一個新的base對象
Child.prototype = new Base("base");
//執行個體化一個child對象
var c1 = new Child("child");
//c1本身具有getId方法
print(c1.getId());
//由於c1從原型鏈上"繼承"到了getName方法,因此可以訪問
print(c1.getName());
得出結果:
child
base
由於遍曆原型鏈的時候,是有下而上的,所以最先遇到的屬性值最先返回,通過這種機制可以完成重載的機制。
this指標
JavaScript中最容易使人迷惑的恐怕就數this指標了,this指標在傳統OO語言中,是在類中聲明的,表示對象本身,而在JavaScript中,this表示當前上下文,即調用者的引用。這裡我們可以來看一個常見的例子: 複製代碼 代碼如下://定義一個人,名字為jack
var jack = {
name : "jack",
age : 26
}
//定義另一個人,名字為abruzzi
var abruzzi = {
name : "abruzzi",
age : 26
}
//定義一個全域的函數對象
function printName(){
return this.name;
}
//設定printName的上下文為jack, 此時的this為jack
print(printName.call(jack));
//設定printName的上下文為abruzzi,此時的this為abruzzi
print(printName.call(abruzzi));
運行結果:
jack
Abruzzi
應該注意的是,this的值並非函數如何被聲明而確定,而是被函數如何被調用而確定,這一點與傳統的物件導向語言截然不同,call是Function上的一個函數,詳細描述在第四章。
使用對象
對象是JavaScript的基礎,我們使用JavaScript來完成編程工作就是通過使用對象來體現的,這一小節通過一些例子來學習如何使用JavaScript對象:
對象的聲明有三種方式:
◆ 通過new操作符範圍Object對象,構造一個新的對象,然後動態添加屬性,從無到有的構築一個對象。
◆ 定義對象的“類”:原型,然後使用new操作符來批量的構築新的對象。
◆ 使用JSON,這個在下一節來進行詳細說明
這一節我們詳細說明第二種方式,如: 複製代碼 代碼如下://定義一個"類",Address
function Address(street, xno){
this.street = street || 'Huang Quan Road';
this.xno = xno || 135;
this.toString = function(){
return "street : " + this.street + ", No : " + this.xno;
}
}
//定義另一個"類",Person
function Person (name, age, addr) {
this.name = name || 'unknown';
this.age = age;
this.addr = addr || new Address(null, null);
this.getName = function () {return this.name;}
this.getAge = function(){return this.age;}
this.getAddr = function(){return this.addr.toString();}
}
//通過new操作符來建立兩個對象,注意,這兩個對象是相互獨立的實體
var jack = new Person('jack', 26, new Address('Qing Hai Road', 123));
var abruzzi = new Person('abruzzi', 26);
//查看結果
print(jack.getName());
print(jack.getAge());
print(jack.getAddr());
print(abruzzi.getName());
print(abruzzi.getAge());
print(abruzzi.getAddr());
運行結果如下:
jack
26
street : Qing Hai Road, No : 123
abruzzi
26
street : Huang Quan Road, No : 135
JSON及其使用
JSON全稱為JavaScript對象標記法(JavaScript Object Notation),即通過字面量來表示一個對象,從簡單到複雜均可使用此方式。比如: 複製代碼 代碼如下:var obj = {
name : "abruzzi",
age : 26,
birthday : new Date(1984, 4, 5),
addr : {
street : "Huang Quan Road",
xno : "135"
}
}
這種方式,顯然比上邊的例子簡潔多了,沒有冗餘的中間變數,很清晰的表達了obj這樣一個對象的結構。事實上,大多數有經驗的JavaScript程式員更傾向與使用這種標記法,包括很多JavaScript的工具包如jQuery,ExtJS等都大量的使用了JSON。JSON事實上已經作為一種前端與伺服器端的資料交換格式,前端程式通過Ajax發送JSON對象到後端,伺服器端指令碼對JSON進行解析,還原成伺服器端對象,然後做一些處理,反饋給前端的仍然是JSON對象,使用同一的資料格式,可以降低出錯的機率。
而且,JSON格式的資料本身是可以遞迴的,也就是說,可以表達任意複雜的資料形式。JSON的寫法很簡單,即用花括弧括起來的索引值對,索引值對通過冒號隔開,而值可以是任意的JavaScript對象,如簡單對象String,Boolean,Number,Null,或者複雜物件如Date,Object,其他自訂的對象等。
JSON的另一個應用情境是:當一個函數擁有多個傳回值時,在傳統的物件導向語言中,我們需要組織一個對象,然後返回,而JavaScript則完全不需要這麼麻煩,比如:
複製代碼 代碼如下:function point(left, top){
this.left = left;
this.top = top;
//handle the left and top
return {x: this.left, y:this.top};
}
直接動態構建一個新的匿名對象返回即可:
複製代碼 代碼如下:var pos = point(3, 4);
//pos.x = 3;
//pos.y = 4;
使用JSON返回對象,這個對象可以有任意複雜的結構,甚至可以包括函數對象。
在實際的編程中,我們通常需要遍曆一個JavaScript對象,事先我們對對象的內容一無所知。怎麼做呢?JavaScript提供了for..in形式的文法糖:
複製代碼 代碼如下:for(var item in json){
//item為鍵
//json[item]為值
}
這種模式十分有用,比如,在實際的WEB應用中,對一個頁面元素需要設定一些屬性,這些屬性是事先不知道的,比如:
複製代碼 代碼如下:var style = {
border:"1px solid #ccc",
color:"blue"
};
然後,我們給一個DOM元素動態添加這些屬性:
複製代碼 代碼如下:for(var item in style){
//使用jQuery的選取器
$("div#element").css(item, style[item]);
}
當然,jQuery有更好的辦法來做這樣一件事,這裡只是舉例子,應該注意的是,我們在給$("div#element")添加屬性的時候,我們對style的結構是不清楚的。
另外比如我們需要收集一些使用者的自訂設定,也可以通過公開一個JSON對象,使用者將需要設定的內容填入這個JSON,然後我們的程式對其進行處理。
複製代碼 代碼如下:function customize(options){
this.settings = $.extend(default, options);
}