先說一下,我們需要達到的目標.
- 當
model的data發(fā)生變化時,dom元素的value要發(fā)生變化. - 當
dom元素的value,發(fā)生變化時,model的data要發(fā)生變化.

核心思想即是:
- 我們需要監(jiān)控到
model.data的set,當執(zhí)行set的時候,改變對應的dom.value - 我們需要監(jiān)控到
dom.value的set,當執(zhí)行到set的時候,改變對應的model.value
1.Object.defineProperty 設(shè)置自定義對象的 set
由于,我們需要監(jiān)控到model.value的set.
常規(guī)做法,一般是不能做到了.
let model = {}
model.value = 'value' // 這里設(shè)置上了就設(shè)置上了,沒有辦法獲取到設(shè)置到的動作.
使用 Object.defineProperty來監(jiān)聽model.value屬性的set行為.
let value = undefined
let model = {}
Object.defineProperty(model, 'value', {
set: function (newVal) {
// 在這檢控到了model.value值的set設(shè)置時機
value = newVal
console.log('model.value setted!!!')
},
get: function () {
return value
}
})
model.value='aaaaa'
model.value setted!!!
可以正常監(jiān)控到 model.value 屬性的 set 時機.
2.dom是HTMLElement對象,能用 Object.defineProperty來設(shè)置某個屬性的set嗎?
由于 dom 元素,本質(zhì)也是一個 HTMLElement對象.
所以,如果.value 屬性內(nèi)部設(shè)置的configurable:true 的話,我們是可以利用Object.defineProperty 來重新設(shè)置 .value 的 set ,拿到dom的.value 修改時機的.
按照一樣的邏輯,使用Object.defineProperty來設(shè)置dom元素的value的set時機
// 拿到input的dom對象
let defaultInputVal = undefined
let input = document.querySelector('#input')
Object.defineProperty(input, 'value', {
set: function (newVal) {
defaultInputVal = newVal
console.log('input.dom.value setted!')
},
get: function () {
return defaultInputVal
}
})

結(jié)果發(fā)現(xiàn),使用同樣的辦法,在dom上無法使用 Object.defineProperty拿到屬性.
如果其內(nèi)部定義 value 屬性設(shè)置的 configurable:false 的話,那么在我這里再次定義,也沒有報錯誤:
Cannot redefine property:value
反正不管怎么樣,自定義對象的那套 Object.defineProperty 想利用 set 方式拿到 DOM 對象某個屬性的 set 時機是拿不到了.
3.利用事件拿到 dom 對象屬性的 set 時機
那么,就退而求其次的使用事件吧.
(反正最終是要拿到dom對象的.value屬性賦值的那個時機)
let input = document.querySelector('#input')
input.addEventListener('keyup', function (e) {
console.log(e.target.value)
}, false)

實現(xiàn)數(shù)據(jù)的雙向綁定.
現(xiàn)在我們有的:
- 利用
Object.defineProperty拿到的了model.value的set時機 - 利用
事件拿到了dom元素的.value的set時機. - 接下來的就簡單了,在
model.value.set內(nèi)部同時修改dom.value - 在
dom.value.set內(nèi)部修改model.value
代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Object.defineProperty</title>
</head>
<body>
<h3>利用Object.defineProperty & dom.event 實現(xiàn)數(shù)據(jù)雙向綁定</h3>
<input type="text" id="input">
<div id="contentText"></div>
</body>
<script>
let value = undefined
let contentText = document.querySelector('#contentText')
let model = {}
Object.defineProperty(model, 'value', {
set: function (newVal) {
// 在這檢控到了model.value值的set設(shè)置時機
value = newVal
// 把model的新值賦值給dom
input.value = newVal
contentText.textContent = newVal
},
get: function () {
return value
}
})
let input = document.querySelector('#input')
input.addEventListener('keyup', function (e) {
// console.log(e.target.value)
// 在這里拿到了dom的value更新值
// 把dom的新值賦值給model
model.value = e.target.value
console.log(`input的數(shù)據(jù)改變了,model.value=${model.value}`)
}, false)
</script>
</html>
效果:

4.補充

其實本質(zhì)上,只要拿到了model.value的set設(shè)置時機,在結(jié)合dom本身支持的事件機制,就能實現(xiàn)數(shù)據(jù)的雙向綁定.
ES6 新推出的 Proxy 也能監(jiān)控到 Obj 的 set,所以也能完成這樣的雙向綁定.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>利用 Proxy 來實現(xiàn)數(shù)據(jù)的雙向綁定</title>
</head>
<body>
<h3>利用ES6提供的Proxy來實現(xiàn)數(shù)據(jù)屬性雙向綁定</h3>
<input type="text" id="input">
<div id="textContent"></div>
</body>
<script>
let input = document.querySelector('#input')
let textContent = document.querySelector('#textContent')
input.addEventListener('keyup', function (e) {
proxyModel.value = e.target.value
console.log(`model.value=${proxyModel.value}`)
}, false)
let model = {
value: undefined
}
let proxyModel = new Proxy(model, {
get: function (obj, prop) {
return obj[prop]
},
set: function (obj, prop, val) {
obj[prop] = val
input.value = val
textContent.textContent = val
}
})
</script>
</html>
結(jié)果:
總結(jié)
雙向綁定實現(xiàn)起來其實非常簡單.無非就下面三步.
- 利用
Proxy或者Object.defineProperty都能設(shè)置屬性的set - 利用
Event機制,拿到dom元素設(shè)置值的時機. - 在雙方對應的
set時機里來實現(xiàn)數(shù)據(jù)的雙向綁定.