一、基本实现
<body>
<input type="text" id='input'>
<span id="show"></span>
</body>
<script>
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function(){
return obj
},
set: function(newValue){
document.getElementById('input').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.getElementById('input').addEventListener('keyup', function(e){
obj.txt = e.target.value
})
</script>
解析其原理:
当通过input
进行输入时,span
的文本值会相应更新;当通过控制台改变obj.txt
的值时,input
输入框内的值也会相应改变,从而实现了view=>model,model=>view的双向绑定。
不过这种简单绑定不会真正执行
obj.txt = e.target.value
,因为在setter
内部没有保存新值,获取obj.txt
时,getter
始终返回{}
。
如果在setter
中赋值obj.txt = e.target.value
,则会造成无限循环(在setter
内部给obj.txt
赋值,一修改值又进入setter
,又给obj.txt
赋值,又进入setter
。。。最终栈溢出)。
为了解决这个问题,将Object.defineProperty()
封装为一个函数,即可在其中保存状态obj.txt
,修改如下:
<body>
<input type="text" id='input'>
<span id="show"></span>
</body>
<script>
function defineProperty(obj, attr){
var val;
Object.defineProperty(obj, attr, {
get: function(){
return val
},
set: function(newValue){
if(newValue === val) return;
val = newValue
document.getElementById('input').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
}
var obj = {}
defineProperty(obj, 'txt')
document.getElementById('input').addEventListener('keyup', function(e){
obj.txt = e.target.value
})
</script>
这就基本实现了一个最简单的双向绑定。