瞭解Object.defineProperty()
Object.defineProperty()方法直接在一個物件上定義一個新屬性,或者修改一個已經存在的屬性, 並返回這個物件。
vueJS採用 ES5 提供的 Object.defineProperty() 方法,監控對資料的操作,從而可以自動觸發資料同步。並且,由於是在不同的資料上觸發同步,可以精確的將變更傳送給繫結的檢視,而不是對所有的資料都執行一次檢測。
首先我們得先知道,ECMAScript中有兩種屬性:資料屬性和訪問器屬性( ie8以下只能在dom物件上使用;不能使用在普通物件上)
資料屬性:
[[Configurable]]: 表示能否修改屬性。預設值為true
[[Enumerable]]: 表示屬性是否可列舉,也就是是否可以通過for-in迴圈返回屬性。預設值為true
[[Writable]]: 表示能否修改屬性的值。預設值為true
[[value]]: 包含這個屬性的值.讀取屬性的時候就是通過這裡開始讀。預設值為undefined
訪問器屬性:
[[Configurable]]: 表示能否修改屬性。預設值為true
[[Enumerable]]: 表示屬性是否可列舉,也就是是否可以通過for-in迴圈返回屬性。預設值為true
[[Get]]: 在讀取屬性時呼叫的函式,預設時undefined
[[Set]]: 在設定屬性時呼叫的函式,預設時undefined
我們要是想修改預設屬性的值就可以使用:Object.defineProperty(obj,prop,descriptor);
1.基本用法:
var a= {}
Object.defineProperty(a,"b",{
value:123
});
console.log(a.b);//123
2.引數介紹:
第一個引數obj:目標物件a
第二個引數prop:需要定義的屬性或方法的名字"b"
第二個引數descriptor:目標屬性所擁有的特性
2.1 第三個引數的取值介紹(descriptor)
value:屬性的值
writable:如果為false,屬性的值就不能被重寫,只能為只讀了
configurable:總開關,一旦為false,就不能再設定他的(value,writable,configurable)
enumerable:是否能在for...in迴圈中遍歷出來或在Object.keys中列舉出來。
get:後面介紹
set:後面介紹
注意:在 descriptor 中不能同時設定訪問器(get 和 set)和 wriable 或 value,否則會錯,就是說用 get 和 set,就不能用 writable 或 value 中的任何一個
在基本用法裡只設定了value,沒有設定別的,可以簡單的理解為(暫時這樣理解)它會預設幫我們把writable,configurable,enumerable。都設上值,而且值還都是false。(僅限於第一次設定的時候),等同於以下程式碼:
var a = {};
Object.defineProperty(a, `b`, {
value: 123,
writable: false,
enumerable: false,
configurable: false
});
console.log(a.b); //123
2.1.1 configurable介紹
總開關,第一次設定 false 之後,,第二次什麼設定也不行了:
也就是說,你可以使用Object.defineProperty()方法無限修改同一個屬性,但是當把configurable改為false之後就有限制了
var a = {};
Object.defineProperty(a, `b`, {
configurable: false
});
Object.defineProperty(a, `b`,{
configurable: true
});
//報錯:Uncaught TypeError: Cannot redefine property: b(…)
2.1.2 writable介紹
var a = {};
Object.defineProperty(a, `b`, {
value: 123,
writable: false //只讀
});
console.log(a.b); // 列印 123
a.b = 124; // 沒有錯誤丟擲(在嚴格模式下會丟擲,即使之前已經有相同的值)
console.log(a.b); // 列印 123, 賦值不起作用。
2.1.3 enumerable介紹
var a = {}
Object.defineProperty(a,"b",{
value:3445,
enumerable:true
});
console.log(Object.keys(a));// 列印["b"]
//改成false:
var a = {}
Object.defineProperty(a,"b",{
value:3445,
enumerable:false
});
console.log(Object.keys(a));// 列印[]
2.1.4 set & get
訪問器屬性不能直接定義!只能通過Object.defineProperty()來定義:
var a= {}
Object.defineProperty(a,"b",{
set:function(newValue){
console.log("賦值是:"+newValue)
},
get:function(){
console.log("取值:")
return 2 //注意這裡,我硬編碼返回2
}
});
a.b =1; //賦值是: 1
console.log(a.b) ; //取值 2
簡單來說,這個 b 賦值或者取值的時候會分別觸發 set 和 get 對應的函式
3.Object.defineProperty示例:
//判斷是不是物件
function isObj(obj){
var type = Object.prototype.toString.call(obj);
return type === `[object Object]`;
}
//執行函式:
function objFun(obj){
if(isObj(obj)){
new Observer(obj);
}
}
function Observer(obj){
this.data = obj;
this.walk(obj);
}
//監聽事件函式:
Observer.prototype.walk = function(obj){
for(var k in obj){
def(obj,k,obj[k])
}
}
function def(obj,k,val){
Object.defineProperty(obj,k,{
configurable:true,
enumerable:true,
get:function(){
console.log(`get取值`);
return val;
},
set:function(newVal){
if(val === newVal){
return;
}
val = newVal;
console.log(`set設定值`)
}
});
}
//測試:
var obj = {a:111,b:222};
objFun(obj);
console.log(obj.a)//get取值 222
obj.a = 333;//set設定值
console.log(obj)
4.Object.defineProperty實現資料和檢視的聯動:
html:
<div>
Object.defineProperty實現資料和檢視的聯動: <br>
<span id="nickName"></span>
<div id="introduce"></div>
</div>
js:(檢視控制器)
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
}
});
//console.log(userInfo)
userInfo.nickName = `我是nickName`;
userInfo.introduce = `我是introduce`
上面設定userInfo的nickName屬性時會呼叫set方法,更新DOM節點的HTML
系列文章的目錄:
Vue雙向繫結的實現原理系列(一):Object.defineproperty
Vue雙向繫結的實現原理系列(二):設計模式
Vue雙向繫結的實現原理系列(三):監聽器Observer和訂閱者Watcher
Vue雙向繫結的實現原理系列(四):補充指令解析器compile