JavaScript obj
轉載自:http://www.cnblogs.com/pingchuanxin/p/5773326.html 平川心
Object(對象)是在所有的編程語言中都十分重要的一個概念,對於事物我們可以把他們看作是一個對象,而每一個事物都有自己的表示的屬性和對於某一信息作出的相應的操作。而這些東西就變成了事物的屬性和方法。
在JS中我們可以見到的對象常量有如下的形式:
1 var obj= { 2 3 name:"Arvin", 4 5 lastName:"Huang" , 6 7 whatsName:function(){ 8 9 alert(this.name+" "+this.lastName); 10 11 }, 12 13 }
由上面的代碼我們可以看出實際上在JS中的對象就是一個以鍵值對形式存儲屬性的一個集合,每一個屬性有一個特定的名稱,並與名稱相對應的值。其實這種關系是有一個專有名稱的,我們可以稱之為映射,當然對於對象來說,除了可以通過這種方式來保持自有屬性,還可以通過繼承的方式來獲取繼承屬性。這種方式我們稱作“原型式繼承”。
接下來我們將從js對象的屬性,屬性特性,方法和對象特性等多個方面來學習對象的概念。
一:js對象創建與對對象屬性。
我們先看如下的代碼。
1 var built = function(){ 2 3 var me = this; 4 me.name = "Arvin"; 5 6 } 7 8 built.prototype = { 9 toStr:function(value){ 10 alert("there have a " + value); 11 } 12 13 }; 14 15 var obj = new built();
上面的代碼就描述了一種我們常常使用到的一種創建對象的方法,使用new關鍵字來創建一個擁有獨立內存區域和指向原型的指針的對象。當我們使用的new的時候,js解析器會分配一塊內存空間,用以存放當前的對象的自有屬性。之后解析器會給這一對象一個_proto_屬性指向的原型對象內容。
還有一種方式我們稱之為對象直接量申明的方式,代碼如下:
1 var obj = { 2 3 name:"Arvin", 4 5 toStr:function(value){ 6 alert("there has a "+value); 7 } 8 };
對象直接量就是直接通過花括號包裹的鍵值對的形式來定義當前對象的。每兩個值之間的通過逗號來進行分割。鍵和值之間通過冒號來分割。放解析器讀取到當前的內容的時候會自動的生成一個對象的內容並把當前的對象存儲在當前上下文中。
還有一種對象創建的方式是使用Object.create()方法,這一方法是ECMAScript5中定義的一個內容,它是一個靜態方法,其使用方法如下。
1 var obj = Object.create({x:1,y:2});//obj繼承了屬性x和y
這一方法傳入的參數是一個對象,並且這一作用的對象將會作為新的對象的原型存在。
接下來我們來看一看屬性。
當我們在chrome的命令台中運行第一段示例代碼的時候我們可以看到如下的內容。
可見,當我們在構造函數中直接使用this指針(指代當前的對象)添加屬性,這時,其實我們的設置的屬性是當前的對象特有的屬性,獨屬於當前的對象的,這樣的屬性稱之為自有屬性。而在我們定義構造函數的時候,我們為構造函數的prototype屬性(指向原型對象),這時我們定義了prototype的內容和方法。當使用new關鍵字來進行對象的構造的時候,我們所構造的對象實際上是繼承了這一原型對象的內容的,所以我們可以再對象的_proto_屬性中可以看見繼承與原型對象的內容,但這也是屬於當前的對象的,(原型鏈內容請閱讀原型鏈學習匯總。)這樣的屬性我們稱之為繼承屬性。
我們對象屬性的類型可以是字符串,數字,true,false,null和undefined,或者是對象的內容。當然雖然字符串,數字,和布爾值雖然不是對象,但是性質和不可變對象類似的。
我們之前說過屬性的值是名字和值,其中名字是可以是包括空字符串在內的任意字符串,但是屬性的值是除這些之外的,還可以是定義好的存取器方法。如下代碼:
1 var o = { 2 x:1,3 y:1,4 5 get r(){return x+y;}, 6 set r(value){this.x = value;} 7 }
如上代碼可見,在對象O中r就是一個存取器屬性內容,存取器屬性其實是不可設置的屬性的一種,只有當他擁有了getter方法的時候才可以取值的,而又setter方法的時候表示這一屬性是可寫的,存取其屬性實在ECMAScript5中才有定義的,實際上是把屬性通過函數的方式進行與外界的數據交互的。從而使得屬性值本身不可以直接進行配置或是獲取。
存取器屬性的寫法就如上面代碼書寫的一樣,get關鍵字空格之后跟屬性名稱作為函數的名稱,set方法和get方法是一樣編寫的,只是會傳入參數,並且參數的個數嚴格是一個,否則會報語法錯誤。在使用的時候我們是通過o.r來調用getter方法,而setter方法的調用時如下,o.r = 2;
當我們沒有定義setter方法的時候,使用對象調用setter方法的時候雖然不會報出錯誤,但是對象中沒有任何變化。而當我們在定義對象的時候沒有定義getter方法的時候,在我們調用這一屬性的時候,將會得到undefined。
二:js對象屬性操作
1.訪問屬性。
對象屬性的訪問一般是通過obj.attr的方式來訪問的,或者是obj[attr]的方式來進行操作,一般情況之下這樣都是可以行的通的,但是但我們的是屬性名稱是一些特殊字段的額時候就要注意了,例如關鍵字或是保留字段,這是后我們要通過中括號的形式來訪問才可以成功,當然這點在ECMAScript5中的已經更改了,但是在我們平常編寫的時候還是要注意變量的命名。對於保留字段盡量不要使用。
屬性訪問的時候,當對象存在而對象中沒有這一屬性的時候,如果程序中訪問了當前的屬性的話我們,js的解析器將會返回undefined的數值,但是如果當前需要訪問的對象是不存在的,這個時候js就會返回一個類型錯誤。
2.屬性賦值。
我們可以通過賦值運算來為屬性中可寫的屬性賦值,當訪問的對象存在,但是表達式中的屬性不存在時候,則會返回一個類型錯誤。如下
1 var obj = { 2 3 this.name = "Arvin"; 4 5 }; 6 7 obj.lastName = "Huang"; //此時對象將會被賦予一個新的屬性為lastName並且其值為Huang
但是雖然說null和undefined在js中是特有的對象,但是我們也不可以為其設置屬性,因為他們都是只讀的。而上一段代碼中的原理其實就是這個。obj.lastName不存在,所以js返回的額是undefined對象,之后再計算賦值,瀏覽器會為其添加新的屬性來存儲當前賦予的值。
當然有一些對象屬性雖然不能賦值但是我們對其進行賦值的時候還是不會報錯,例如如下代碼:
1 Object.prototype = 0;//復制失敗但是並不會報錯。object原名沒有被修改。
在ECMAScript中的嚴格模式下面已經得到了修復。
我們對於對象中的屬性賦值出錯的情況會有如下的總結。
- O中的屬性是只讀的時候,我們對於當前屬性是只有訪問權限而沒有復制權限的。
- O中的某一屬性為繼承屬性的時候,我們是不能對其進行賦值的。
- O中不存在自有屬性P,沒有繼承Setter方法的時候,並且O對象是不可擴展的,則當前的對象是不可定義新的屬性的。
3.屬性刪除:
delete運算符可以刪除對象中的屬性。這里先講一下delete運算符的內容。
delete:一元運算符,用於刪除對象和數組中的屬性的,單它也僅僅是刪除一個值,並不會返回刪除的內容。
1 var o = {x:1, y:2};//定義了一個對象 2 delete o.x; 3 "x" in o; //這里將會得到false值。 4 5 var a = [1,2,3]; 6 delete a[1]; 7 2 in a; //元素2已經不再數組中了。 8 9 a.length == 3 //這里將會顯示為true,雖然刪除了屬性但是,留下了一個占位,所以數組長度沒有變化。
這是在chrome中運行的結果,我們可以看見,刪除屬性之后其實只是元素本身與對象的關系被斷開了,但是實際上數組中的內容數量還是沒有改變的,但是並沒有數值,所以通過undefined來進行填充。
delete操作數需要時一個左值(對象的屬性,變量),如果不是的話將會不做任何操作返回true。如下圖:
圖片中可見,返回的值是true,但是7並不是一個左值。
當然也有一些delete沒有辦法刪除的左值。
由此可見,delete是沒有辦法刪除用var關鍵字定義的變量是不可以刪除的。在delete刪除元素失敗的時候會返回一個false值。當然我們使用群居變量定義的屬性也是可以通過delete對象來刪除的,因為全局對象也是對象嗎。
好接下來回歸正題。對象通過delete實際上就是斷開當前的對象和屬性之間的聯系。當我們刪除了屬性之后還要查找這一屬性的時候,我們會得到的值是undefined這個值。當然delete只能刪除自有屬性,對於繼承屬性delete是沒有作用的。如果想刪除繼承屬性的話,直接在對原型對象屬性進行刪除不就好了。。還有一點,當對象的屬性是不可配置的時候也是不刪除的(但是當對象是不可配置的,但是其屬性是可以配置的時候,還是可以刪除的。對象的特性內容請看后文。)
當然在用delete的時候還是要注意的應為有些情況下將會出現一些問題。如下代碼。
1 var o= {x:1, y:2}; 2 var b = o.x; 3 delete o.x; 4 //這個時候b實際上還是只想之前的x的內容的,這種情況很容易導致內存泄漏的額問題。
4.檢測屬性:
我們經常要判斷某一個屬性是否存在於某一個對象中。這個時候我們可以通過in運算符,hasOwnProperty()方法或是propertylsEnumerable()方法來進行判斷。
首先我們來講一下IN運算符的內容:in操作符是一個二元操作符,其左邊的額數值是字符串或者是可以轉化為字符串的,右邊的內容是對象。判斷的當前的屬性是不是存在於對象中。
1 var point = {x:1}; 2 3 "x" in point; //這一個表達式最后返回的將會是true。 4 "toString" in point; //由於toString是繼承方法,所以也是返回true. 5 "z" in point; //這一表達式最后返回false,因為point對象中沒有z屬性. 6 7 //數組可以通過索引來判斷當前的數組中是否有相應的數據。
所以我們可以通過使用in操作符來判斷當前的屬性是不是存在與某一對象中的。並且即使是繼承屬性,也是可以測試的。
第二種檢測的方法是hasOwnProperty()方法。代碼如下:
1 var o ={x:1}; 2 3 o.hasOwnProperty("x"); //true:o有這一屬性, 4 o.hasOwnProperty("y"); //false; 5 o.hasOwnProperty("toString"); //false
所以我們可以知道,hasOwnProperty方法只能測試當前屬性是不是對象的自有屬性。
第三種檢測方式是用propertylsEnumerable()方法。只有當當前的屬性是自有屬性,並且是可枚舉的的時候,這一方法才會返回true。
5.枚舉屬性:
遍歷屬性將會是進場要用到的內容。
- for/in循環遍歷當前的對象的內容是一種很常見的手段。其可以遍歷對象中的所有的可枚舉屬性,包括當前對象的自有屬性和繼承屬性。
- Object.key()方法,枚舉屬性名稱的函數,他返回的是一個數組,其中存在的是對象中的可枚舉屬性名稱組成。
- Object.getOwnPropertyNames()方法,其返回的額也是數組,但是是所有的自有屬性名稱的數組。
三:js屬性特性:
屬性的特性其實就是值當前的屬性是否可以寫可以讀等等。即外部對象對於屬性操作的權限。
當前的js一般的屬性都是有4中屬性。分別是:數值屬性value,可讀屬性writable,可枚舉屬性enumerable,和可配置屬性configurable。但是由於對象中存在一類特別的屬性存取器屬性,所以對於存取器屬性的值實際上是有點不同的,他有自己的特別的屬性特性包括,讀取(get),寫入(set),可枚舉和可配置。為了實現這一對象屬性的描述,js中定義了一個屬性描述符對象。並且可以通過Object.getOwnPropertyDescriptor()方法來獲取某個對象中的特定屬性的描述符。當然當前函數只能獲取對象自有屬性的描述,如果要獲取繼承屬性的描述符的話,需要使用Object.getPrototypeOf();
當然我們可以使用Object.defineProperty方法進行對象內容的進行相關的編輯,如下
1 Object.defineProperty({},"x", {value:1, writable:true, enumerable:true, configurable:true}); 2 //這是將返回一個對象,並且其中設定了一個可讀寫,枚舉和配置的屬性x 3 4 //當然如果要修改的對象本身其中就有一個這一屬性,然后想通過這一方法配置的話。也是可以的。 5 6 //特性對象中的內容可以不用寫全,當添加屬性的時候未寫明的內容將會直接設置成為false或是undefined。而在修改屬性的時候未寫明的內容將不會有任何改變。
當然我們也可以用Object.defineProperties()來進行對個屬性的修改和添加,這是我們需要一個對應列表。如下代碼
1 Object.defineProperties({}, { 2 x:{value:1, writable:true}, 3 y:{value:2, writable:true}, 4 ...... 5 });
當我們在定義一個對元素的屬性的時候,我們要注意上面兩個方法在某些情況之下是會報錯誤的,情況如下
- 對象是不可擴展的,所以我們只能對原有的自有屬性進行編輯,但是不可以添加新的屬性。
- 如果屬性是不可配置的,則不可以修改他的可配置性和可枚舉性。
- 存儲器屬性是不可配置的,則不可以修改他的setter和getter屬性,並且不可以改成數據屬性。
- 數據屬性不可配置則不能修改為存儲器屬性。
- 數據屬性是不可配置情況下,其可寫屬性不可改false為true,但是可以修改true為false。
- 如果數據屬性是不可配置不可寫,不能修改其值,但是可配置不可寫,則可以修改。
var obj = { x:1, get y(){return x;}, set y(value){x = value;} }; console.log(""+obj.hasOwnProperty("y")); console.log(Object.getOwnPropertyNames(obj).toString()); Object.defineProperty(obj,"x",{value:1, writable:true, enumerable:false, configurable:true});//修改當前屬性為不可枚舉的 console.log(Object.getOwnPropertyNames(obj).toString()); console.log(""+obj.hasOwnProperty("x")); console.log(""+obj.propertyIsEnumerable("x")); obj.x = 2; console.log(obj.x); Object.defineProperty(obj,"x",{value:1, writable:false, enumerable:true, configurable:true});//修改當前屬性為不可寫的 console.log(Object.getOwnPropertyNames(obj).toString()); console.log(""+obj.hasOwnProperty("x")); obj.x = 2; console.log(obj.x); Object.defineProperty(obj,"x",{value:1, writable:true, enumerable:true, configurable:false});//修改當前屬性為不可配置的 console.log(Object.getOwnPropertyNames(obj).toString()); console.log(""+obj.hasOwnProperty("x")); obj.x = 3; console.log(obj.x); Object.defineProperty(obj,"x",{value:1, writable:false, enumerable:false, configurable:true});//測試不可配置屬性是否可以改變屬性特性 console.log(Object.getOwnPropertyNames(obj).toString()); console.log(""+obj.hasOwnProperty("x")); obj.x = 3; console.log(obj.x);
運行結果如下:
四:js對象方法。
我們這里主要說明的將會是js的Object.prototype里面的原生的方法。
1、toString()方法,是我們常用到的一個方法,當我們要將對象轉換為值的時候,都會調用這么一個方法,但是其實現,實際上只是返回了一些對象的信息。例如:
1 var s = {x:1, y:1}.toString() 2 //這里返回的內容是[Object, Object]
當然在許多的內置對象中toString方法實際上是被重寫了的,例如,array(數組)對象中,我們是童toString方法的話,是吧當前的額數組中的內容以逗號隔開的形式來返回字符串的。函數調用toString方法的時候,是返回函數的源代碼。還有Data對象是toString方法返回的是當前時間星系字符串,等等。
2、toLocaleString方法和toString方法是類似的,只是返回的是本地的字符串,實際上就是根據一些當地的用戶使用習慣來定義的返回內容。
3、toJSON()方法,實際上在Object.prototype中是沒有這一方法的。我們常用的多的是在Date對象中使用,當需要獲取當前對象的序列化的時候調用他它怎會獲得當前對象的序列化。
4.valueOf()方法,類似於toString()方法,實際上在js只有在要吧當前的對象轉換為費字符串的情況之下才會調用這一方法。一般的對象調用這一方法的時候,返回的數值,實際上就是當前對象的內容,當然內置對象也有改變了這一方法的實現的。例如Date.valueOf(),返回的就是從1970年1月1日到現在的毫秒數。
五:js對象的特性。
1、原型屬性:原型屬性實際上就是當前的對象繼承自哪一個對象,當前對象繼承了原型對象中的方法和屬性,所以我們稱之為對象的原型屬性,當然也可以稱之為對象的原型。當然我們也有一個方法測試某一對象是否是繼承與另一對象,使用isPrototypeOf()方法來進行判斷。
2、類的屬性:其實其本身是一個字符串來着,用以表示對象的類型,上文中的提到的toString方法可以獲取類的屬性。當然由於toString在許多的內置對象中有重寫,所以黨我們調用toString的方法的時候最好使用的如下封裝方法。
1 function classof(o){ 2 if(o === null){ 3 return "Null"; 4 } 5 if( o === undefined){ 6 return "Undefined"; 7 } 8 return Object.prototype.toString.call(o).slice(8, -1); 9 }
3、可擴展性:對象的可擴展性表示的是但錢的對象是否是可以擴展的,宿主對象的可擴展性是有但錢的額js的引擎來進行定義的,所有的額內置對象都是可以擴展的,除非我們把其轉化成為不可擴展的對象,Object.esExtensible()可以用來進行判斷的當前的對象是否是可以擴展到的。Object.preventExtensions()方法設置當前的兌現格式不可以擴展的內容。當我們把對象設置成為不可擴展之后,我們就不可以在准換當前的內容。實際上上述的方法只是對於當前對象的自由空間來定義的,所以黨我們改變源性對象的時候當前的對象中實際上還是會有一定的變化的。當然還有一些其他的方法是可以來避免對象的受到外來的干擾的。Object.seal()其實和之前的preventExtensible()方法還是挺像的,除了將但前的對象設置成為不可設置的,同事吧其所有的額屬性也同時設置成為不可設置的。還有一個函數更絕,Object.freeze()方法不僅僅把對象的屬性和本身設置成為不可配置的,同事也設置為不可寫的狀態,所以所設置完成之后整個就一只讀對象。
4.序列化對象:實際上就是指當前的對象轉化為字符串,當然也是可以還原的。序列化當前的對象實際上就是通過JSON來進行操作的。其中將Object轉化成為json字符串的方法是,JSON.stringify(),其中的參數是需要序列化的對象。而JSON.parse()則是反序列化,但實際上這樣得出的對象是序列化的深拷貝。當然JSON只是js的一個子集,支持對象,數組,字符串,無窮大數字,true,false,null。NaN,infinity等等這一類數據在序列化的時候都會統一轉化成為null,而Date的數據在序列化的時候是會直接轉換成為ISO格式的日期字符串,就類似於Date.toJSON()方法一樣的。
轉自:wusifan的博客