淺談JavaScript的面向物件程式設計(一)
面向物件的語言有一個標誌,他們都有類的概念,通過類可以建立多個具有相同屬性和方法的物件。但是JavaScript中沒有類的概念,因此JavaScript與其他的面嚮物件語言還是有一定區別的。JavaScript把物件定義為無序屬性的集合,其屬性可以包含基本值、物件或者函式。物件的每個屬性或者方法都有一個名字,而每個名字都對映到一個值。所有我把JavaScript的物件看成一組無序的鍵值對。
物件是什麼
以前曾介紹過物件的建立,建立物件最簡單的方式就是建立Object物件的一個例項,再為他新增屬性和方法。
1 var obj = new Object(); 2 obj.name="test"; 3 obj.getName=function(){ 4 return this.name; 5 } 6 console.log(obj.getName());
上面的程式碼第一行建立了一個物件obj,第二行給obj添加了屬性name,第三行給obj添加了方法getName。通過getName方法能夠獲取obj屬性name的值。第六行輸出字串test。
1 var obj = {};
2 obj.name="test";
3 obj.getName=function(){
4 return this.name;
5 }
6 console.log(obj.getName());
上面的程式碼通過字面量建立了物件obj。其他與通過Object建立的完全一樣。
物件的屬性
JavaScript有兩種屬性:資料屬性和訪問器屬性。
- 資料屬性
資料屬性包含一個數據值的位置。在這個位置可以讀取和寫入值。資料屬性有4個描述其行為的特性。
Configurable:表示能否通過delete關鍵字刪除屬性或者把屬性修改為訪問器屬性。configurable預設是true。
Enumerable:表示能否通過for-in迴圈,預設值為true。
Writable:表示能夠修改屬性的值,預設為true。
Value:包含這個資料的屬性值。讀取屬性值時,從這個位置讀。寫入屬性值的時候,把新值儲存在這個位置。預設為undefined。
對於像前面那樣定義物件,他的configurable、enumerable和writable預設都是true,value是指定給它的值。
1 var obj={
2 name:"test";
3 }
這建立的物件obj擁有一個屬性,並設定value值為test。要修改預設屬性必須使用Object.defineProperty()方法。這個方法接收三個引數:屬性所在的物件、屬性的名字和描述符。描述符物件的屬性必須是configurable、enumerable、writable和value中的一個或者多個。
1 var obj={
2 };
3 Object.defineProperty(obj,"name",{
4 configurable:true,
5 value:"hehe",
6 enumerable:true,
7 writable:false
8 });
9 obj.name="test";
10 console.log(obj.name);//輸出hehe
這個例子建立了一個屬性name,並設定wriable為false,也就是說name屬性的value將無法寫入。所在我在第九行修改name的值,並沒有效果。name的值依然為test。
把configurable設定為false之後,該屬性就將無法通過delete刪除,同時也不能通過Object.defineProperty()方法設定除writable之後的值。在嚴格模式下,如果呼叫該方法將會報錯。
當通過Object.defineProperty方法定義屬性的時候,configurable、enumerable和writable特性的預設值是false。所以,最好不要通過該方法定義屬性。
- 訪問器屬性
訪問器屬性不包含value,但是包含一對getter和setter函式。在讀取訪問器屬性時,會呼叫getter函式。在寫入訪問器屬性時,會呼叫setter函式。訪問器屬性有以下四個特性:
Configurable:表示能否通過delete關鍵字刪除屬性或者把屬性修改為訪問器屬性。configurable預設是true。
Enumerable:表示能否通過for-in迴圈,預設值為true。
get:在讀取屬性的時候呼叫的函式,預設值為undefined。
set:在寫入屬性的值時呼叫的函式,預設是undefined。
1 var person={"_name":"hehe","age":18}
2 Object.defineProperty(person,"name",{
3 get:function(){
4 return this._name;
5 },
6 set:function(str){
7
8 if(this._name!="haha"){
9 this._name=str;
10 this.age="19";
11 }
12 }
13 });
14 person.name="haha";
15 console.log(person.name+":"+person.age);//haha:19
上面的程式碼建立了一個物件person,並定義了兩個屬性_name和age。_name表示內部屬性,一般由物件本身去訪問。定義了屬性name,有get和set方法。不一定要同時定義get和set方法。只有get方法,則嘗試寫入屬性的時候報報錯。只有set方法,則獲取屬性的時候會報錯。通過Object的defineProperties能夠同時定義多個屬性。
1 var obj={};
2 Object.defineProperties(obj,{
3 name:{
4 value:"hehe"
5 },
6 age:{
7 value:"18"
8 }
9 });
10 console.log(obj.name+":"+obj.age);//hehe:18
上面的程式碼,通過Object的defineProperties方法同時定義了兩個屬性name和age。該方法的第一個引數是物件,第二個引數是由需要定義的屬性組成的物件。
- 讀取屬性的特性
JavaScript提供了Object.getOwnPropertyDescriptor方法,可以獲取屬性的描述符。這個方法需要兩個引數:第一個引數是物件,第二個引數是描述符屬性的名稱。返回值是一個物件,如果是訪問器屬性則有configurable、enumerable、writable、get和set。如果是資料屬性,這個物件有屬性configurable、enumerable、writable和value。
1 var obj={};
2 Object.defineProperties(obj,{
3 name:{
4 value:"hehe"
5 },
6 age:{
7 value:"18"
8 },
9 _sex:{
10 value:"man"
11 },
12 sex:{
13 get:function(){
14 return this._sex;
15 },
16 set:function(str){
17 this._sex=str;
18 }
19 }
20 });
21 var descriptor1=Object.getOwnPropertyDescriptor(obj,"name");
22 var descriptor2=Object.getOwnPropertyDescriptor(obj,"sex");
23 console.log(descriptor1.writable);//false
24 console.log(descriptor2.get);//function
25 console.log(obj.name+":"+obj.age+","+obj.sex);//hehe:18,man
上面的程式碼,通過Object.getOwnPropertyDescriptor()方法獲取屬性的描述嗎,分別獲取了資料屬性以及訪問器屬性。