JavaScript中屬性和特性是完全不同的兩個概念,這裡我將根據自己所學,來深入理解JavaScript中的屬性和特性。
主要內容如下:
第一部分:理解JavaScript中對象的本質、對象與類的關系、對象與引用類型的關系
對象的本質:ECMA-262把對象定義為:無序屬性的集合,其屬性可以包含基本值、對象或者函數。即對象是一組沒有特定順序的值,對象的每個屬性或方法都有一個名字,而這個名字都映射到一個值。故對象的本質是一個散列表:其中是一組名值對,值可以是數據或函數。
對象和類的關系:在JavaScript中,對象和類沒有任何關系。這是因為ECMAScript中根本就沒有類的概念,它的對象與其他基於類的語言中的對象是不同的。
對象和引用類型的關系:對象和引用類型並不是等價的,因為每個對象都是基於一個引用類型創建的。
第二部分:對象屬性如何進行分類
由構造函數或對象字面量方法創建的對象中具有屬性和方法(只要提到屬性和方法,它們一定是屬於對象的;只要提到對象,它一定是具有屬性和方法的(自定義除外)),其中屬性又可分為數據屬性和訪問器屬性,他們的區別如下:
數據屬性一般用於存儲數據數值,訪問器屬性不包含數據值
訪問器屬性多用於get/set操作
第三部分:屬性中特性的理解
ECMAScript為了描述對象屬性(property)的各種特征,定義了特性(attribute)這個概念。也就是說特性不同於屬性,特性是為了描述屬性的。下面,我將分別講解:
1.數據屬性及其特性
剛剛我們說過,數據屬性是用於存儲數據數值的,因此數據屬性具有一個數據值的位置,在這個位置可以讀取和寫入值。數據屬性有4個描述其行為的特性,由於ECMAScript規定:在JavaScript中不能直接訪問屬性的特性(注意:不是不能訪問),所以我們把它放在兩組方括號中。如下:
這些特性都具有默認值,但是如果這些默認值不是我們想要的,該怎麼辦呢?當然就是修改啦!我們可以通過Object.defineProperty()方法來修改屬性默認的特性。英文difineProperty即為定義屬性的意思。這個方法接收三個參數:屬性所在的對象、屬性的名字和一個描述符對象。其中第三個參數描述符對象是對象字面量的方法創建的,裡面的屬性和屬性值實際上保存的是要修改的特性和特性值。
下面通過幾個例子來深入理解。
a
var person={}; Object.defineProperty(person,"name",{ writable:false, value:"zhuzhenwei" }); console.log(person.name);//zhuzhenwei person.name="heting"; console.log(person.name);//zhuzhenwei
這裡我用對象字面量的方法創建了一個對象,但是沒有同時創建方法和屬性。而是利用了Object.defineProperty()方法來創建了屬性和修改了默認值。這裡將writable設置為false,於是後面我試圖修改person.name時,是無效的。
b
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei" }); console.log(person.name);//zhuzhenwei person.name="heting"; console.log(person.name);//zhuzhenwei
注意看這個例子,這個例子中我刪去了writable:false,為什麼還是不能修改呢?這是因為之前我在介紹特性時,前三個默認為ture,是在創建對象並創建屬性的情況下得到的。對於通過調用Object.defineProperty()方法創建的屬性,其前三個特性的默認值均為false,這裡需要注意。
c
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:false }); console.log(person.name);//zhuzhenwei delete person.name; console.log(person.name);//zhuzhenwei
這裡我們將新建的屬性name的特性設置為了configurable:false;因此下面刪除屬性的操作是無效的。根據b,可知configurable,默認就是false,即使去掉也不可修改。
d
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//zhuzhenwei delete person.name; console.log(person.name);//undefined
在這裡我將默認的configurable的值由默認的false修改為了true,於是變成了可配置的,那麼最後就成功刪除了。
e
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:false }); console.log(person.name);//zhuzhenwei Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
如果之前已經設置成為了false,那麼後面再改成true也是徒勞的,即:一旦把屬性設置成為不可配置的,就不能再把它變回可配置了。
f
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…) var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", }); console.log(person.name);//zhuzhenwei Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
這裡可以說明,即使前一步我們不管默認的configurable:false,後面得到的仍是不可配置。於是,可以得出結論,為了可配置,必須在第一次調用Object.defineProperty()函數時就將默認的值修改為true。
2.訪問器屬性及其特性
之前提到,訪問器屬性不包含數據值,他們包含一對getter函數和setter函數(這兩個函數不是必須的)。在讀取訪問器屬性時,會調用getter函數,這個函數負責返回有效的值;在寫入訪問器屬性是,會調用setter函數並傳入新值,這個函數負責決定如何處理數據。同樣,由於不能通過JavaScript來直接訪問得到訪問器屬性的特性,所以下面列出的特性將由[[]]括起來以作區分。
注意:1.相對於數據屬性,我們發現訪問器屬性中沒有writable特性和value特性。這是因為訪問器屬性不包含數據值,那麼我們怎麼當然就不可修改屬性的值(用不到writable特性),更不用考慮value了。
2.訪問器屬性不能直接定義,必須是用Object.defineProperty()來定義。(通過這個規定我們就能准確地判斷出訪問器屬性和數據屬性了)
通過下面這個例子來深入理解:
var book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){<br> return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }); book.year=2005; console.log(book.edition);//2
幾個需要深入理解的地方:
3.如何利用Object.defineProperties()方法定義多個特性
顯然,一個對象不可能只具有一個屬性,因此,定義多個屬性的可能性很大,於是JavaScript提供了Object.defineProperties()方法解決這個問題。這個方法接收兩個參數,第一個是要定義屬性所在的對象,第二個是一個對象字面量方法創建的對象,對象的屬性名即為要定義的特姓名,對象的屬性值又是一個對象,這個對象裡的屬性名和屬性值分別是特性名和特性值(這裡不是很好理解,看例子即可)。
var book={}; Object.defineProperties(book,{ _year:{ writable:true, value:2004 }, edition:{ writable:true, value:1 }, year:{ get:function(){ return this._year; }, set:function(){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } } });
4.如何利用Object.getOwnPropertyDescripter()方法讀取屬性的描述符以讀取屬性的特性
我們可以使用Object.getOwnPropertyDescripter()方法來取得給定屬性的描述符。getOwnPropertyDescripter即為取得自身屬性描述符的意思。這個方法接收兩個參數:屬性所在的對象要要讀取其描述符的屬性名稱。返回一個對象。
對於訪問器屬性而言,這個對象的屬性有configurable、enumerable、get和set;
對於數據屬性而言,這個對象的屬性有configurable、enumerable、writable和value。
var book={}; Object.defineProperties(book,{ _year:{ value:2004 }, edition:{ value:1 }, year:{ get:function(){ return this._year; }, set:function(){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } } }); var descriptor=Object.getOwnPropertyDescriptor(book,"_year"); console.log(descriptor.value);//2004 console.log(descriptor.configurable);//false 因為通過Object.defineProperties()方法創建的屬性的特性configurable enumerable都是false console.log(typeof descriptor.get);//undefined 注意:這是數據屬性,是不具有get特性的 var descriptor=Object.getOwnPropertyDescriptor(book,"year"); console.log(descriptor.value);//undefined console.log(descriptor.enumerable);//false console.log(typeof descriptor.get);//function get雖然是屬性的一個特性,但是它也是函數。
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,同時也希望多多支持!