什麼是雙向綁定?html
1.當一個對象(或變量)的屬性改變,那麼調用這個屬性的地方顯示也應該改變,模型到視圖(model => view)vue
2.當調用屬性的這個地方改變了這個屬性(一般是一個表單元素),那麼這個對象(或變量)的屬性也會改成最新的值 ,即視圖到模型(view => model)數組
那麼vue利用es5的defineProperty特性實現雙向綁定的原理是什麼呢?ide
例如:函數
var person= {}; Object.defineProperty(person, "name", { value: '張三' }) console.log(person.name); // 張三
傳參this
第一個參數:要設置的目標對象(必填)es5
第二個參數:須要定義的屬性或方法的名稱(必填)spa
第三個參數:目標屬性所擁有的特性。(descriptor)(必填)prototype
三個參數都是必填項,重點介紹第三個參數 descriptor翻譯
descriptor
value:屬性的值
writable:若是爲false,屬性的值就不能被重寫, 只能爲只讀了
configurable:總開關,一旦爲false,就不能再設置他的(value,writable,configurable)
enumerable:是否可枚舉(是否能在for...in循環中遍歷出來或在Object.keys中列舉出來)
get:後面會重點講解
set:後面會重點講解
var person = {}; Object.defineProperty(person ,"name",{ value: '張三', writable :false, enumerable: false, configurable: false }); console.log(person.name); // 張三
這裏須要注意的是!!!!
總開關,第一次設置 false 以後,,第二次什麼設置也不行了。
若是設置爲fasle,就變成只讀了。
屬性特性 enumerable 定義了對象的屬性是否能夠在 for...in 循環和 Object.keys() 中被枚舉。
在 descriptor 中不能同時設置訪問器(get 和 set)和 wriable 或 value,不然會錯,就是說想用 get 和 set,就不能用 writable 或 value 中的任何一個。
var user = {}; var defaultName = "狂奔的蝸牛"; Object.defineProperty(user,"name",{ get:function(){ console.log("你是否是來獲取值啦"); return defaultName; }, set:function(value){ console.log("你是否是來設置值啦"); defaultName = value; } }) console.log(user.name); user.name = "狂奔的蘿蔔"; console.log(user.name);
每當我獲取user.name屬性時,get方法被調用,get 方法對應的函數被執行,輸出 你是否是來獲取值啦;每當我設置user.name屬性時,set方法對應的函數被執行,輸出 你是否是來設置值啦 ; 是的,咱們監控到了代碼對user.name屬性的存取。
模型到視圖(model => view)的同步
說明 假設id="model" 的元素的 value 是user.name的值,既然咱們能夠在改變屬性的執行日誌輸出(console.log("你是否是來設置值啦");),那麼,咱們在設置值的時候給id="model" 的元素設置下新值,不就實現了從模型到視圖
<body> 手寫一個簡單雙向綁定<br/> <input type="text" id="model"><br/> <div id="modelText"></div> </body> <script> var user = {}; var defaultName = "狂奔的蝸牛"; document.querySelector("#model").value = defaultName; document.querySelector("#modelText").textContent = defaultName; //定義屬性 監控改變 Object.defineProperty(user,"name",{ get:function(){ console.log("你是否是來獲取值啦"); return defaultName; }, set:function(newValue){ console.log("設置新值"); defaultName = newValue; console.log("實現 模型 => 視圖"); document.querySelector("#model").value = newValue; document.querySelector("#modelText").textContent = newValue; } }) console.log("2s 後改變值"); setTimeout(() => { //改變值 user.name = "狂奔的蘿蔔"; }, 2000); </script>
視圖到模型(view => model)的同步
問: 咱們能捕捉到view對值更改嗎?
答:能夠!! id="model" 的input元素的 value 是user.name的值,填充在這個文本框裏面,文本框有個「 keyup」 事件,當咱們在文本框中輸入文字的時候,文本框的值會跟着改變,而且會連續觸發keyup事件,那麼咱們只須要監聽這個事件,是否是就能夠捕捉到view對值的更改了??既然文本框的值會跟着改變,咱們獲取最新的值再把新值更新到user.name屬性,不就實現了視圖到模型(view => model)的同步
<body> 手寫一個簡單雙向綁定<br/> <input type="text" id="model"><br/> <div id="modelText"></div> </body> <script> var user = {}; var defaultName = "狂奔的蝸牛"; var model = document.querySelector("#model"); var modelText = document.querySelector("#modelText"); model.value = defaultName; modelText.textContent = defaultName; //定義屬性 監控改變 Object.defineProperty(user,"name",{ get:function(){ console.log("你是否是來獲取值啦"); return defaultName; }, set:function(newValue){ console.log("設置新值"); defaultName = newValue; model.value = newValue; modelText.textContent = newValue; } }) model.addEventListener("keyup", function () { user.name = this.value; console.log("實現 視圖 => 模型"); }, false) </script>
【最終源碼】
在上述代碼的基礎上,加入了 用戶輸入中文的判斷(用戶輸入中文時,頻繁觸發 keyup事件,但實際上輸入並無結束。)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>雙向綁定</title> </head> <body> 手寫一個簡單雙向綁定<br/> <input type="text" id="model"><br/> <div id="modelText"></div> </body> <script> var model = document.querySelector("#model"); var modelText = document.querySelector("#modelText"); var defaultName = "defaultName"; var userInfo = {} model.value = defaultName; Object.defineProperty(userInfo, "name", { get: function () { return defaultName; }, set: function (value) { defaultName = value; model.value = value; console.log("-----value"); console.log(value); modelText.textContent = value; } }) userInfo.name = "new value"; var isEnd = true; model.addEventListener("keyup", function () { if (isEnd) { userInfo.name = this.value; } }, false) //加入監聽中文輸入事件 model.addEventListener("compositionstart", function () { console.log("開始輸入中文"); isEnd = false; }) model.addEventListener("compositionend", function () { isEnd = true; console.log("結束輸入中文"); }) </script> </html>
這樣基於object.definePropety特性就完成了雙向綁定。
下面咱們再來看一下object.definePropety的用法及其餘屬性
Object.defineProperty()
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
Object.defineProperty(obj, prop, descriptor)
obj
要在其上定義屬性的對象。
prop
要定義或修改的屬性的名稱。
descriptor
將被定義或修改的屬性描述符。
被傳遞給函數的對象。
該方法容許精確添加或修改對象的屬性。經過賦值操做添加的普通屬性是可枚舉的,可以在屬性枚舉期間呈現出來(for...in
或 Object.keys
方法), 這些屬性的值能夠被改變,也能夠被刪除。這個方法容許修改默認的額外選項(或配置)。默認狀況下,使用 Object.defineProperty()
添加的屬性值是不可修改的。
對象裏目前存在的屬性描述符有兩種主要形式:數據描述符和存取描述符。數據描述符是一個具備值的屬性,該值多是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是二者。
數據描述符和存取描述符均具備如下可選鍵值:
configurable
描述符
纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。
enumerable
enumerable
爲true
時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。
數據描述符同時具備如下可選鍵值:
value
undefined
。
writable
writable
爲true
時,value
才能被賦值運算符改變。默認爲 false。
存取描述符同時具備如下可選鍵值:
get
undefined
。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,可是會傳入this
對象(因爲繼承關係,這裏的this
並不必定是定義該屬性的對象)。
undefined
。
set
undefined
。當屬性值修改時,觸發執行該方法。該方法將接受惟一參數,即該屬性新的參數值。
undefined
。
記住,這些選項不必定是自身屬性,若是是繼承來的也要考慮。爲了確認保留這些默認值,你可能要在這以前凍結 Object.prototype
,明確指定全部的選項,或者經過 Object.create(null)
將__proto__
屬性指向null
。