Javascript实现双向数据绑定
近几年前端技术栈真是发展的太迅速了,从以前的针对dom操作的框架如jquery,ext.js等框架逐步过渡到当前的mvvm模式,让前端开发者将注意力从dom操作逐渐解脱出来,专注于逻辑的实现,很大程度提升了开发效率,mvvm模式的一个核心便是数据的双向绑定。
# 什么是双向绑定
双向绑定即View与Model相互关联,相互影响。一般情况下,我们通过Model数据取更新View, 那如果用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定。
在浏览器中,当用户修改了表单的内容时,我们绑定的Model会自动更新:
# 双向绑定原理
以Vue为例,Vue中的数据双向绑定主要是采用数据劫持结合发布者-订阅者模式的方式使用Object.defineProperty()来劫持每个属性的getter和setter,当数据在变化时发布消息给订阅者,触发相应的监听回调。
# Object.defineProperty()
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
语法:
Object.defineProperty(obj, prop, descriptor)
参数:
- obj: 要在其上定义属性的对象
- prop: 要定义或修改的属性的名称
- descriptor:将被定义或修改的属性描述符
返回值:
- 被传递给函数的对象
# 属性描述符
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符均具有以下可选键值(默认值是在使用Object.defineProperty()定义属性的情况下):
configurable
当且仅当该属性的 configurable 为 true 时,该属性
描述符
才能够被改变,同时该属性也能从对应的对象上被删除。enumerable
当且仅当该属性的
enumerable
为true
时,该属性才能够出现在对象的枚举属性中。
数据描述符同时具有以下可选键值:
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable
当且仅当该属性的
writable
为true
时,value
才能被赋值运算符改变。
存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为
undefined
。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this
对象(由于继承关系,这里的this
并不一定是定义该属性的对象),默认为 undefined。set
一个给属性提供 setter 的方法,如果没有 setter 则为
undefined
。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值,默认为 undefined。
描述符可同时具有的键值
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
数据描述符 | Yes | Yes | Yes | Yes | No | No |
存取描述符 | Yes | Yes | No | No | Yes | Yes |
如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
# 示例
# 代码实现
<!DOCTYPE html>
<html>
<head>
<title>ES6测试</title>
<meta name="keywords" content="ES6测试">
<meta name="description" content="ES6测试">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="applicable-device" content="mobile">
</head>
<body>
<div id="app">
<h3 id="showText"></h3>
<input type="text" name="text" id="text">
</div>
<script type="text/javascript">
var data = {};
Object.defineProperty(data, 'name', {
get:function() {
},
set:function(val) {
document.getElementById('showText').innerHTML = val;
document.getElementById("text").value = val;
}
});
document.getElementById("text").addEventListener('keyup', function(e){
data.name = e.target.value;
})
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39