強大的JS方法Object.defineProperty詳解及VUE.JS雙向綁定原理

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
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末饱须,一起剝皮案震驚了整個濱河市域醇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冤寿,老刑警劉巖歹苦,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件青伤,死亡現(xiàn)場離奇詭異督怜,居然都是意外死亡,警方通過查閱死者的電腦和手機狠角,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門号杠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丰歌,你說我怎么就攤上這事姨蟋。” “怎么了立帖?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵眼溶,是天一觀的道長。 經常有香客問我晓勇,道長堂飞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任绑咱,我火速辦了婚禮绰筛,結果婚禮上,老公的妹妹穿的比我還像新娘描融。我一直安慰自己铝噩,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布窿克。 她就那樣靜靜地躺著骏庸,像睡著了一般毛甲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敞恋,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天丽啡,我揣著相機與錄音,去河邊找鬼硬猫。 笑死补箍,一個胖子當著我的面吹牛,可吹牛的內容都是我干的啸蜜。 我是一名探鬼主播坑雅,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衬横!你這毒婦竟也來了裹粤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜂林,失蹤者是張志新(化名)和其女友劉穎遥诉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體噪叙,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡矮锈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了睁蕾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苞笨。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖子眶,靈堂內的尸體忽然破棺而出瀑凝,到底是詐尸還是另有隱情,我是刑警寧澤臭杰,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布粤咪,位于F島的核電站,受9級特大地震影響渴杆,放射性物質發(fā)生泄漏寥枝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一将塑、第九天 我趴在偏房一處隱蔽的房頂上張望脉顿。 院中可真熱鬧,春花似錦点寥、人聲如沸艾疟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔽莱。三九已至弟疆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盗冷,已是汗流浹背怠苔。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仪糖,地道東北人柑司。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像锅劝,于是被迫代替她去往敵國和親攒驰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容