首先介紹了JavaScript
對象數據屬性與訪問器屬性的相關概念,而後介紹了屬性定義與讀取的相關方法,最後對JavaScript
對象數據屬性與訪問器屬性的知識作了一些擴展,並手動實現了一個簡單的數據雙向綁定實例。javascript
文中若是有疏漏錯誤之處,各位可在文末評論中提出。html
建立JavaScript
對簡單的方法有兩種:一是直接建立一個object
實例,而後爲他添加屬性與方法,二是經過對象字面量語法的方式建立。vue
new object
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function(){
alert(this.name);
};
複製代碼
上述實例建立了一個名爲person的實例對象,併爲其添加了三個屬性(name
,age
,job
)和一個方法(sayName
)。java
上述示例一樣可經過對象字面量的語法實現以下。dom
var person = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function() {
alert(this.name);
}
};
複製代碼
對於1.1
與1.2
的建立的每一個實例都有本身的屬性和方法,這些屬性在建立時都帶有一些特徵值,這些特徵值僅供內部使用,在JavaScript
中不能直接訪問他們,按規定這些特徵值須要放在兩對方括號中,例如[[Enumerable]]
。ide
ECMAScript
中有兩類屬性,分別是數據屬性
與選擇器屬性
函數
數據屬性包含一個數據值的位置。在這個位置能夠讀取和寫入值。數據屬性有 4 個描述其行爲的 特性。ui
[[Configurable]]
:表示可否經過delete
刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性。[[Enumerable]]
:表示可否經過for-in循環返回屬性。[[Writable]]
:表示可否修改屬性的值。[[Value]]
:包含這個屬性的數據值。經過
new object
或者對象字面量方式建立對象定義的屬性,他們的[[Configurable]]
、[[Enumerable]]
、[[Writable]]
特徵均爲true
,而[[Value]]
值被設置爲指定的值。this
var person = {
name: "Nicholas"
}
複製代碼
以上代碼經過對象字面量的方式定義了一個person
對象,併爲它指定了name
屬性,此時name
屬性的四個特徵值,[[Configurable]]
、[[Enumerable]]
、[[Writable]]
特徵均爲true
,而[[Value]]
爲Nicholas
。spa
要修改屬性默認的特性,必須使用Object.defineProperty()方法,可同時修改一個或多個特性值,該方法接收三個參數:
object
:屬性所在的對象propertyName
:屬性的名字descripor
:屬性的描述符,必須是[[Configurable]]
、[[Enumerable]]
、[[Writable]]
、[[Value]]
中的一個或者多個值。var person = {
name: fanweiren
};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
複製代碼
以上代碼將name
屬性的值設置爲設置爲Nicholas
,並將name
屬性的[[writable]]
特性值設爲false
,表示該屬性只可讀不可修改。若是嘗試爲它指定新值,非嚴格模式下,賦值操做會被忽略,在嚴格模式下,會拋出錯誤。
var person = {
name: fanweiren
};
Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
delete person.name;
alert(person.name); //"Nicholas"
複製代碼
把[[Configurable]]
特性設置爲false後,屬性name
既不能從對象中刪除,此時調用delete在非嚴格模式下什麼也不會發生,在嚴格模式下會拋出錯誤。
一旦把
[[Configurable]]
特性設置爲false即不可配置後,就不能在變回可配置了。且設置爲false後,[[Enumerable]]
、[[Writable]]
也都不能修改了。
發現一個奇怪的現象,
[[Configurable]]
特性設置爲false
後,若是[[Writable]]
之前爲true
,則還能夠將其修改成false
,若是[[Writable]]
之前爲false
,則不可修改。
訪問器屬性不包含數據值;它們包含一對兒getter
和setter
函數(不過,這兩個函數都不是必需的)。在讀取訪問器屬性時,會調用getter
函數,這個函數負責返回有效的值;在寫入訪問器屬性時,會調用 setter
函數並傳入新值,這個函數負責決定如何處理數據。訪問器屬性有以下 4 個特性。
[[Configurable]]
:特性同1.1數據屬性的該特徵值[[Enumerable]]
:特性同1.1數據屬性的該特徵值[[Get]]
:在讀取屬性時調用的函數。默認值爲 undefined。[[Set]]
:在寫入屬性時調用的函數。默認值爲 undefined。訪問器屬性不能直接定義,必須使用 Object.defineProperty()
來定義。請看下面的例子。
var book = {
hideYear: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function(){
return this.hideYear;
},
set: function(newValue){
if (newValue > 2004) {
this.hideYear = newValue;
this.edition += newValue - 2004;
}
}
});
book.year = 2005;
alert(book.edition); //2
複製代碼
以上代碼定義了hideYear
與edition
兩個數據屬性,同時定義了year
這個訪問器屬性。year
訪問器屬性包含get
與set
兩個方法,get
方法返回hideYear
屬性的值,set
方法經過計算來肯定正確的版本。
get和set能夠不一樣時使用,但在嚴格模式下只其中一個,會拋出錯誤
三點總結:
new object
或者對象字面量爲對象添加的屬性均爲數據屬性,訪問器屬性必須用Object.defineProperty()
定義。new object
或者對象字面量爲對象添加的屬性,其[[Configurable]]
、[[Enumerable]]
、[[Writable]]
屬性默認均爲true
,經過Object.defineProperty()
定義的屬性,對於defineProperty
方法沒定義的特徵,默認爲false
。// 經過`new object`或者對象字面量爲對象添加的屬性
var person = {
name: 'fanweiren'
} // configurable、enumerable、writable均爲true
// 經過`Object.defineProperty()`定義的屬性
Object.defineProperty(book, "name", {
value: 'fanweiren'
} // configurable、enumerable、writable均爲false
Object.defineProperty(book, "name", {
configurable: true
value: 'fanweiren'
}// configurable爲true,enumerable、writable爲false
複製代碼
var obj = {};
Object.defineProperty(obj, 'a', {
value: 'a1',
get: function() {
return 'a2'
} // 錯誤!數據描述符與存取描述符不可混用
});
複製代碼
vue
雙向綁定數據實現的核心內涵。利用Object.defineProperty()
定義單個屬性。利用Object.defineProperties()
同時定義多個屬性,該方法接收兩個參數,具體使用方式見以下代碼。
var book = {};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
複製代碼
以上代碼爲對象book
定義了三個屬性,其中_year
與edition
爲數據屬性,year
爲訪問器屬性。
Object.getOwnPropertyDescriptor()
方法能夠取得給定屬性的描述符。這個方法返回值是一個對象,若是是訪問器屬性,這個對象的屬性有configurable
、enumerable
、get
和set
;若是是數據屬性,這 個對象的屬性有configurable
、enumerable
、writable
和value
。對於第三部分(定義多個屬性)的代碼,調用Object.getOwnPropertyDescriptor()
方法後結果以下:
var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
console.log(JSON.stringify(descriptor))
//{"value":2004,"writable":false,"enumerable":false,"configurable":false}
alert(typeof descriptor.get); //"undefined"
var descriptor = Object.getOwnPropertyDescriptor(book, "year");
console.log(JSON.stringify(descriptor)) // {"enumerable":false,"configurable":false}
alert(typeof descriptor.get); // function
alert(typeof descriptor.set); // function
複製代碼
configurable
默認爲false全局環境下,a=1
中的a
至關於window
的一個屬性,而var a=1
中的a
至關於定義的一個變量。
a = 1; //a是其所在對象的一個屬性, configurable與對象字面量定義屬性同樣,默認爲true
複製代碼
var a = 1; // 經過var或let初始化的,configurable屬性默認爲false
複製代碼
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input id="input"/></br> <button id="btn">點我修改值</button> <script> let inputNode = document.getElementById('input'); let person = {} Object.defineProperty(person, 'name' ,{ configurable: true, get: function () { console.log('person.name.get():'+ inputNode.value) return inputNode.value }, set: function (newValue) { console.log('person.name.set():' + newValue) inputNode.value = newValue } }) inputNode.oninput = function () { console.log('inputNode.oninput: ' + inputNode.value) person.name = inputNode.value; } let btn = document.getElementById('btn'); btn.onclick = function () { person.name = Math.random() } </script> </body> </html> 複製代碼
以上代碼實現了一個將input
輸入框的數據與perosn
對象的name
屬性值綁定的實例,input
輸入框內的數據發生改變會同步影響perosn.name
的值,而對perosn.name
的任何修改也會同步到input
輸入框內。
注意是將
input
與perosn.name
數據綁定,而不是input
與button
綁定,button
的做用僅僅是改變perosn.name
的值,方便演示而已。 具體步驟爲
input
輸入框與一個button
按鈕perosn.name
定義訪問器屬性的特性,重寫set
與get
方法,set
方法會獲取input
輸入框的值並返回,而set
方法會爲input
輸入框從新賦值。當perosn.name
被賦值時,input
輸入框的值會同步變化。input
輸入框添加監聽方法,input
輸入值發生改變時會被觸發,觸發此方法時input
內的值會被賦值給perosn.name
,致使perosn.name
同步變化。btn
按鈕添加點擊的監聽方法,主要是爲了方便爲perosn.name
賦值,以觀察第3步所說的現象。若是要觀察第2步的現象,能夠按F12
觀察console
頁面的打印數據。下圖爲上述代碼的示意圖。
本文從對象的建立引出對象數據屬性與訪問器屬性的概念,首先介紹了數據屬性與訪問器屬性的相關知識,而後介紹了屬性定義與讀取的相關方法。主要結論以下:
javascript
對象的屬性分爲數據屬性與訪問器屬性兩類,經過new object
與對象字面量定義的屬性都爲數據屬性,訪問器屬性必須經過Object.defineProperty()
方法定義。new object
或者對象字面量爲對象添加的屬性,其[[Configurable]]
、[[Enumerable]]
、[[Writable]]
屬性默認均爲true
,經過Object.defineProperty()
定義的屬性,對於defineProperty
方法沒定義的特徵,默認爲false
。[[Configurable]]
表示可否經過delete刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性,[[Enumerable]]
表示可否經過for-in
循環返回屬性。