該方法允許精確添加或修改對象的屬性。通過賦值操作添加的普通屬性是可枚舉的,能夠在屬性枚舉期間呈現出來(for…in 或 Object.keys 方法), 這些屬性的值可以被改變,也可以被刪除。這個方法允許修改默認的額外選項(或配置)。默認情況下,使用 Object.defineProperty() 添加的屬性值是不可修改的。
Object.defineProperty(obj, prop, descriptor)
:
obj
:要在其上定義屬性的對象。prop
:要定義或修改的屬性的名稱。descriptor
:將被定義或修改的屬性描述符。
屬性描述符
對象裏目前存在的屬性描述符有兩種主要形式:數據描述符和存取描述符。數據描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是兩者。
- 數據描述符和存取描述符均具有以下可選鍵值(默認值是在使用Object.defineProperty()定義屬性的情況下):
configurable
:當且僅當該屬性的 configurable 爲 true 時,該屬性描述符才能夠被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。
enumerable
:當且僅當該屬性的enumerable爲true時,該屬性才能夠出現在對象的枚舉屬性中。默認爲 false。 - 數據描述符同時具有以下可選鍵值:
value
:該屬性對應的值。可以是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined。
writable
:當且僅當該屬性的writable爲true時,value才能被賦值運算符改變。默認爲 false。 - 存取描述符同時具有以下可選鍵值:
get
:一個給屬性提供 getter 的方法,如果沒有 getter 則爲 undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,但是會傳入this對象(由於繼承關係,這裏的this並不一定是定義該屬性的對象)。默認爲 undefined。
set
:一個給屬性提供 setter 的方法,如果沒有 setter 則爲 undefined。當屬性值修改時,觸發執行該方法。該方法將接受唯一參數,即該屬性新的參數值。默認爲 undefined。
此外,所有的屬性描述符都是非必須的,但是 descriptor 這個字段是必須的,如果不進行任何配置,你可以這樣:
var obj = Object.defineProperty({}, "num", {});
console.log(obj.num); // undefined
描述符可同時具有的鍵值:
如果一個描述符不具有value,writable,get 和 set 任意一個關鍵字,那麼它將被認爲是一個數據描述符。如果一個描述符同時有(value或writable)和(get或set)關鍵字,將會產生一個異常。
默認值總結:
示例:
var o={};//創建一個新對象
Object.defineProperty(o,"a",{
//該屬性對應的值。可以是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined
value:37,
// 僅當僅當該屬性的writable爲 true 時,該屬性才能被賦值運算符改變。默認爲 false
writable:true,
//僅當該屬性的 enumerable 爲 true 時,該屬性才能夠出現在對象的枚舉屬性中。默認爲 false
enumerable:true,
// 僅當該屬性的 configurable 爲 true 時,該屬性才能夠被改變,也能夠被刪除。默認爲 false
configurable:true
});
// 對象o擁有了屬性a,值爲37
var bValue;
Object.defineProperty(o,"b",{
get:function () { return bValue; },
set:function (v) { bValue=v; },
enumerable:true,
configurable:true
});
o.b=45;
數據和視圖聯動:
給對象o定義新的屬性b,並且定義屬性b的get和set方法,當o.b的時候會調用b屬性的get方法,給b屬性賦值的時候,會調用set方法,這就是修改數據的時候,視圖會自動更新的關鍵。
前端獲取數據後,需要根據數據操作dom,視圖變化後,需要修改不少代碼,有沒有方法將數據和dom操作隔離,看一個例子,顯示用戶信息的html模版。
<div>
<p>你好,<span id='nickName'></span></p>
<div id="introduce"></div>
</div>
//視圖控制器
var userInfo = {};
Object.defineProperty(userInfo, "nickName", {
get: function(){
return document.getElementById('nickName').innerHTML;
},
set: function(nick){
document.getElementById('nickName').innerHTML = nick;
}
});
Object.defineProperty(userInfo, "introduce", {
get: function(){
return document.getElementById('introduce').innerHTML;
},
set: function(introduce){
document.getElementById('introduce').innerHTML = introduce;
}
});
//數據
//todo 獲取用戶信息的代碼
....
userInfo.nickName = "xxx";
userInfo.introduce = "我是xxx,我來自雲南,..."
示例2:
傳統的做法:
<span id="container">1</span>
<button id="button">點擊加 1</button>
document.getElementById('button').addEventListener("click", function(){
var container = document.getElementById("container");
container.innerHTML = Number(container.innerHTML) + 1;
});
使用defineProperty:
var obj = {
value: 1
}
//存儲 obj.value 的值的變量
var value = 1;
Object.defineProperty(obj, "value", {
get: function() {
return value;
},
set: function(newValue) {
value = newValue;
document.getElementById('container').innerHTML = newValue;
}
});
document.getElementById('button').addEventListener("click", function() {
obj.value += 1;
});
現在的寫法,我們還需要單獨聲明一個變量value 存儲 obj.value 的值,因爲如果你在 set 中直接 obj.value = newValue 就會陷入無限的循環中。此外,我們可能需要監控很多屬性值的改變,要是一個一個寫,也很累吶,所以我們簡單寫個 watch 函數。
var obj = {
value: 1
}
watch(obj, "value", function(newvalue){
document.getElementById('container').innerHTML = newvalue;
})
document.getElementById('button').addEventListener("click", function(){
obj.value += 1
});
watch 函數:
(function(){
var root = this;
function watch(obj, name, func){
//存儲value的變量
var value = obj[name];
Object.defineProperty(obj, name, {
get: function() {
return value;
},
set: function(newValue) {
value = newValue;
func(value)
}
});
if (value) obj[name] = value
}
this.watch = watch;
})()
內容來源及參考博文:
深入淺出Object.defineProperty() - 簡書
Object.defineProperty() - [MDN]
Object.defineProperty()方法詳解 - 蔣金柱 - 博客園
Object.defineProperty - 樂樂曹 - 簡書