Object.defineProperty是一個很了不起的方法齐苛。vue.js之所以能夠實現(xiàn)雙向綁定便是拜它所賜带猴!defineProperty直接翻譯過來即是“定義屬性”荚斯,不過該方法可不僅僅是定義屬性這么簡單惩猫,咱們還可以通過它來對屬性進行攔截設置陨晶!
我們知道對象是由多個鍵/值對組成的無序集合猬仁。對象當中的屬性可以是任意類型的值。我們可以通過構造函數(shù)以及字面量的形式來定義對象先誉。
var obj={};//或obj=new Object;
// 添加屬性(描述)
obj.userName="laotie";//或 obj["userName"]="laotie"
// 添加方法(行為)
obj.run=function(){};//或 obj["run"]=function(){};
為對象增加屬性的方法除了上面的方式外湿刽,咱們還可以通過Object.defineProperty來定義新屬性,或者對原屬性進行修改褐耳。
Object.defineProperty()
語法:
- Object.defineProperty(obj, prop, descriptor)
參數(shù)說明:
- obj:必需叭爱。目標對象
- prop:必需。需定義或修改的屬性的名字
- descriptor:必需漱病。目標屬性所擁有的特性
前兩個參數(shù)不多說了买雾,看代碼就明白了把曼,我們主要看第三個參數(shù)descriptor,看看它是個什么鬼漓穿!
1嗤军、 value
通過value可以為對象設置屬性,對應的值可以是任意類型晃危,默認為undefined
var obj={};
console.log(obj.userName);// undefined
Object.defineProperty(obj,"userName",{
value:"laozhang"
});
console.log(obj.userName);// laozhang
可以對原有值進行修改:
var obj={};
obj.userName="laoli"
console.log(obj.userName);// laoli
Object.defineProperty(obj,"userName",{
value:"laozhang"
});
console.log(obj.userName);// laozhang
返回的值為傳入函數(shù)的對象叙赚,即第一個參數(shù)obj:
var obj={};
var obj2=Object.defineProperty(obj,"userName",{
value:"laozhang"
});
console.log(obj==obj2);// true
2、writable
用于設置屬性的值是否允許被重寫僚饭。true為允許震叮,false不允許被重寫,默認為false
設置為false不允許被重寫鳍鸵,并沒有錯誤拋出
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang",
writable:false
});
obj.userName="laoliu";
console.log(obj.userName);// laozhang
如果在嚴格描述下會報錯:
"use strict";
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang",
writable:false
});
obj.userName="laoliu";
//報錯:TypeError: Cannot assign to read only property 'userName' of object
默認為false苇瓣,值不允許被修改
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang"
});
obj.userName="laoliu";
console.log(obj.userName);// laozhang
設置為true,允許被修改
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang",
writable:true
});
obj.userName="laoliu";
console.log(obj.userName);// laoliu
3、enumerable
該描述決定著指定的屬性是否允許被枚舉(使用for...in或Object.keys())偿乖。設置為true可以被枚舉击罪;設置為false,不能被枚舉贪薪。默認為false媳禁。
設置為false不允許被枚舉
var obj={
age:18
};
Object.defineProperty(obj,"userName",{
value:"laozhang",
writable:false,
enumerable:false
});
for(var key in obj){
console.log(key,obj[key])// age 18
}
console.log(Object.keys(obj));//[ 'age' ]
默認值為false, 不允許被枚舉
var obj={
age:18
};
Object.defineProperty(obj,"userName",{
value:"laozhang",
writable:false
});
for(var key in obj){
console.log(key,obj[key])// age 18
}
console.log(Object.keys(obj));//[ 'age' ]
設置為true,允許被枚舉
var obj={
age:18
};
Object.defineProperty(obj,"userName",{
value:"laozhang",
writable:false,
enumerable:true
});
for(var key in obj){
/* age 18
userName laozhang*/
console.log(key,obj[key]);
}
console.log(Object.keys(obj));//[ 'age', 'userName' ]
4、configurable
configurable是一個總開關画切,一旦你將它設置為false竣稽,就不能刪除指定的屬性也不能再設置他的(value,writable霍弹,configurable)丧枪,設置為true止吁,允許被刪除宝与,也允許被設置榨汤。
為false不允許被刪除朝巫,不會報錯
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang",
configurable:false
});
delete obj.userName;
console.log(obj.userName);//laozhang
為false不允許被重新設置耸峭,會報錯
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang",
configurable:false
});
Object.defineProperty(obj,"userName",{
value:"laoli"
});
//報錯:TypeError: Cannot redefine property: userName
為true時贿条,允許被刪除
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang",
configurable:true
});
delete obj.userName;
console.log(obj.userName);//undefined
為true時叨叙,允許被重新設置
var obj={};
Object.defineProperty(obj,"userName",{
value:"laozhang",
configurable:true
});
Object.defineProperty(obj,"userName",{
value:"laoli"
});
console.log(obj.userName);//laoli
5涨冀、get/set存取器描述
當你需要設置或獲取對象的某個屬性值的時候私恬,可以使用該方法债沮。
var obj={};
var initValue="xixi";
Object.defineProperty(obj,"userName",{
get(){
// 當讀取userName時會有輸出
console.log("執(zhí)行了get");
return initValue;
},
set(newValue){
// newValue為寫入的值
console.log("執(zhí)行了set");
initValue= newValue+"吧!"
}
});
/* 執(zhí)行了get
xixi */
console.log(obj.userName);
obj.userName="愛我";// 執(zhí)行了set
/* 執(zhí)行了get
愛我吧本鸣! */
console.log(obj.userName);
get或set不是必須成對出現(xiàn)疫衩,任寫其一就可以。
var obj={};
Object.defineProperty(obj,"userName",{
get(){
return "lala"
}
});
console.log(obj.userName);//lala
當使用了get或set方法荣德,不允許使用writable和value這兩個屬性
var obj={};
Object.defineProperty(obj,"userName",{
value:"xixi",
get(){
return "lala"
}
});
console.log(obj.userName);
//報錯:Invalid property descriptor. Cannot both specify accessors and a value or writable attribute
接下來看個實例:
var obj={};
var userName="";
var userArr=[];
Object.defineProperty(obj,"userName",{
get(){
return userName;
},
set(value){
userArr.push(value);
userName=value;
}
});
obj.userName="張三";
obj.userName="李四";
obj.userName="王五";
console.log(userArr);// [ '張三', '李四', '王五' ]
以上實例通過存取器成功將userName曾經擁有過的值進行了存儲闷煤。是不是很神奇童芹,很簡單?
接下來,咱們可以通過defineProperty模擬下VUE.JS的雙向綁定:
<body>
<input type="text" id="myInp"/>
<div id="myDiv"></div>
</body>
<script>
var myInp=document.querySelector("#myInp");
var myDiv=document.querySelector("#myDiv");
var obj={
v:"haha"
}
myInp.value=obj.v;
myDiv.innerHTML=obj.v;
Object.defineProperty(obj,"v",{
set:function(v){
myDiv.innerHTML=myInp.value=v;
}
})
myInp.onkeyup=function(e){
console.log(e.target.value);
obj.v=e.target.value;
}
</script>
好了鲤拿,馬上就結束了假褪。可能有的小伙伴會想近顷,既然這個Object.defineProperty如此強大生音,每次只能設置一個屬性嗎?那么這玩意兒用起來也挺費勁的窒升!那么現(xiàn)在大咖上場:Object.defineProperties()缀遍。你可以通過該大咖同時設置多個對象描述。
var obj = new Object();
Object.defineProperties(obj, {
name: {
value: '張三',
configurable: false,
writable: true,
enumerable: true
},
age: {
value: 18,
configurable: true
}
})
console.log(obj.name, obj.age) // 張三, 18