在講到Obeject.defineProperty()方法之前先得說明一下ECMAScript中有兩種屬性:數據屬性和訪問器屬性。
兩種屬性存在的意義:描述對象屬性(key)的一些特性,因為這些屬性是內部值,一般放到 [[兩個中括號]] 中。
Object.defineProperty(obj , 'key' , {描述信息,是個對象,類似配置項} ) 方法接收三個參數,屬性所在的對象,屬性名 和一個描述符對象。
數據屬性的描述符是下面的一個或者多個(configurable、enumerable、writable 和 value)
訪問器屬性的描述符是以下的一個或多個(configurable、enumerable、get 和 set)
通過 Object.defineProperty()
方法,可以創建數據屬性(並設定其內部屬性),也可以創建訪問器屬性(訪問器屬性包含 getter
和 setter
函數)
數據屬性的各項解釋:
enumerable : 可枚舉性,是否能通過for-in或者Object.keys來遍歷
writable : 可修改(可寫入),是否能修改value屬性值
value : 屬性的屬性值 (讀出和寫入都是從這里讀出和保存的),默認undefined 數據屬性是可以直接通過 對象.屬性obj.key
的形式訪問和賦值的
configurable : 是否可配置,當它為false的時候,1、上面三項的true和false值都不能改變了,2、對象本身也不能通過delete來刪除3、數據屬性和訪問器屬性也不能來回轉化了
訪問器屬性的各項解釋:
configurable 、enumerable同上
get : 讀取屬性的時候調用,默認值undefined
set : 設置屬性的時候調用,默認值undefined
對比數據屬性和訪問器屬性的屬性異同
數據屬性作用: 修改屬性默認的特性 (會新建出數據屬性)
訪問器的作用: 訪問和設置數據屬性的屬性值,不會創建出 .
1、對象屬性的創建對比----- 這里創建的是數據屬性不是訪問器屬性,訪問器屬性只有一種創建方式
A、字面量創建對象的形式:var obj = {key : value } ------ var person1 = {age : 3} 直接創建了age屬性和屬性值
B、Object.defineProperty(obj ,'key' {...} )創建對象 ------ var person2 = {}; Object.defineProperty(person2, "age", { value: 3 });
通過統一的操作 :
① console.log(person1 .name) ② person1.age = 4 ③ console.log(person1 .name) 得出 // 3 , 4 賦給了新的值
① console.log(person2 .name) ② person2.age = 4 ③ console.log(person2 .name) 得出 // 3 , 3 賦值沒有成功
原理:
直接在對象上定義的屬性var obj = {key : value }(字面量形式和new Object()形式
) ,它們的Configurable、Enumerable 和Writable特性都被設置為 true,而Value特性被設置為指定的值
通過 Object.defineProperty()定義的屬性 它們的Configurable、Enumerable 和Writable特性都被設置為 false,而Value特性被設置為指定的值,這時候的屬性值是只讀的
2、重點:數據屬性不僅可以直接訪問和設置(對象.屬性/obj.key的形式),也可以通過定義的訪問器(getter setter)來專門訪問和設置。
得出結論:
① 通過發現book下面並沒有真正的year屬性,證明了數據屬性並不能根據訪問器屬性來定義創建,訪問器屬性只是操作訪問器屬性,這個屬性可以理解為是在get和set這一動態讀取和設置的過程中發揮作用。
② set
和get
是一對勾子(hook)函數,要理解觸發機制(是根據調用book.year或者設置book.year=2005這個時間點觸發的,所以這個year可以隨便取值):就是訪問器屬性在調用時,實際上對應觸發訪問器屬性中定義的 get
方法(最后返回的值是其中return出來的結果),當對一個對象的某個屬性賦值時,則會自動觸發調用相應的set
函數。(如果不了解運行機制,需要打斷點走一遍即可)
③ book只有_year和edtion兩個數據屬性(是不是數據屬性,要看帶不帶value屬性),year不是數據屬性(訪問屬性不包含數據值,也不會顯式的寫出來),但是但是book.hasOwnProperty("year");
確實返回 true
。說明了訪問器屬性也屬於對象自己添加的屬性。只是讀寫方式和普通的不一樣。
3、再來擼一遍代碼,看看運行的原理具體是怎樣的??


4、怎么區分數據屬性還是訪問器屬性???看里面的描述符對象。兩者是不能混用的。
5、js中的屬性的理解:
javascript中一共有三種屬性:
普通屬性:也就是常規的數據屬性,這種屬性是用戶來添加修改等,把它設置成什么樣,就返回出來什么樣,不會做些額外的事情。 var obj ={name : "yang"};那么console.log(obj.name) //yang
內部屬性:比如數組arr的length屬性,函數的prototype屬性,DOM節點的innerHTML屬性,用戶賦完值以后,取值的時候,不一定按預期,有時候還會額外的做一些事情,也難以改變他們的行為。
比如說某一數組,它的長度為10, 當我們設置它為11時,它就會增加一個undefined元素,再設置為9時,就會從后面刪掉兩個元素。
函數的prototype如果被改變,相當於將其父類改變了,會new不同類型的實例。
DOM的innerHTML,我們賦值時是一個字符串,再取出時,這字符串可能會與原來的不一樣, 並且在原元素上生成了不一樣的子節點。
訪問器屬性,允許用戶在賦值或取值都經過預先設定的函數,從而實現內部屬性的那一種特殊效果。
6、應用(可以代替對象中的getter和setter函數)
==
其他地方:vue框架MVVM的數據雙向綁定,數據的實時觀察,不同於angular使用的臟檢查。
7、和Object.defineProperty()相關的一些對象方法。
obj.propertyIsEnumerable('key')
obj.hasOwnProperty('key') 可以檢測出訪問器屬性
Obeject.getOwnpropertyDescriptor(obj , 'key') 或者描述符對象 ----- 有就返回對象,否則是undefined。
Object.keys(obj)方法,遍歷key值 。 當Object.defineProperty()中enumerable為false的時候,是遍歷不出來的。
8、其他
Obeject.prototype的configurable是false,代表不可配置
對象obj的toString方法是原型鏈上的是不可枚舉的 obj.propertyIsEnumerable(toString') // false
最后:不要再IE8 及以前版本 中使用 Object.defineProperty()