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
# 页面测试效果
