数据绑定——观察者模式


  AngularJs是一款优秀的前端JS框架,它实现了数据模型(data-model)关联到视图(UI)上。但个人认为正是由于它规范性的结构和体系导致使用的时候并不是很灵活。那么如何自己实现一个数据绑定视图的功能呢。

        设想一下这样的应用场景,当我们修改数据或从服务器接收数据更新现有数据时,如何自动通知所有与数据关联的视图更新显示呢?观察者模式为这种场景提供了很好的解决方案。

        观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

        每个HTML Dom都可以看成一个观察者,他们依赖数据对象,类图如图:

wKiom1hsjNOyQDLHAAAayfJjWqg312.png-wh_50


        其中Observer类中doms为观察者的标签们,这里一个观察者包含多个标签,避免创建过多的观察者。Subject类作为数据对象的封装,由于javascript没有符号重载的功能,我们设计Setter和Getter的两个方法S()和G()。具体代码如下:

var Observer = Class.extend({
	doms:[],
	subject:null,
	ctor:function(_tag,_subject){
		
		this.doms = document.querySelectorAll(_tag);
		this.subject = _subject;
		this.subject.attach(this);
	},
	update:function(){
		for(var i = 0;i < this.doms.length;i++){
			var tagName = this.doms[i].nodeName.toLowerCase();
			if(this.doms[i].getAttribute('bind-data') != undefined){
				var bind = this.doms[i].getAttribute('bind-data');
				if(tagName == 'input'||tagName == 'select'){
					if(typeof(this.subject.G()) == "object" && Object.prototype.toString.call(this.subject.G()).toLowerCase() == "[object object]" && !this.subject.G().length){
						this.doms[i].value = eval("this.subject.G()." + bind);
					}else{
						this.doms[i].value = this.subject.G();
					}
				}else{
					if(typeof(this.subject.G()) == "object" && Object.prototype.toString.call(this.subject.G()).toLowerCase() == "[object object]" && !this.subject.G().length){
						this.doms[i].innerText = eval("this.subject.G()." + bind);
					}else{
						this.doms[i].innerText = this.subject.G();
					}
				}
			}
		}
	}
});
var Subject = Class.extend({
	observers:[],
	data:null,
	ctor:function(_data){
		this.data = _data;
	},
	attach:function(_observer){
		this.observers.push(_observer);
		_observer.update();
	},
	S:function(expre){
		if(typeof(expre) == 'string'){
			
			if(expre.indexOf('=') == -1){
				this.data = expre;
			}else{
				if(typeof(this.data) == "object" && Object.prototype.toString.call(this.data).toLowerCase() == "[object object]" && !this.data.length){
					eval('this.data.' + expre);
				}else{
					this.data = {};
					eval('this.data.' + expre);
				}
			}
		}else{
			this.data = expre;
		}
		this.notifyAllObservers();
	},
	G:function(){
		return this.data;
	},
	notifyAllObservers(){
		for(var i = 0;i < this.observers.length;i++){
			this.observers[i].update();
		}
	}
	
});

前端使用的代码如下:

<body>
		用户名:<input var = 'user' bind-data = 'username' id = "username"/>
		密码:<input var = "user" bind-data = 'password' id = "username"/>
		<p>用户名:<span id = "username" bind-data="username"></span></p>
		<button onclick = "changeUserName()">修改用户名为jack</button>
		<p>{username:'hello',password:'world'}</p>
		<button onclick = "changeAll()">修改数据为上述值</button>
		<p>将数据赋值为非JSON对象值</p>
		<button onclick = "changeSingleV()">修改为非对象值</button>
	</body>
	<script>
		var user = new Subject({username:'janwool',password:123456});
		var observer_input = new Observer("[var='user']",user);
		var observer_span = new Observer("#username",user);
		function changeUserName(){
			user.S("username='jack'");
		}
		function changeAll(){
			user.S({username:'hello',password:'world'});
		}
		function changeSingleV(){
			user.S('janwool');
		}
	</script>


效果如图:

wKioL1hsjWqQBmweAAAg-_5RCvo529.png-wh_50


根据结果,我们可以清楚的发现观察者的优势,数据的更新前端开发人员无需再操作Dom就可以更新视图,实现了数据实时更新。当然缺点也显而易见,那就是观察者过多时会数据的更新会过慢。


本文出自 “走一停二回头看三” 博客,请务必保留此出处http://janwool.blog.51cto.com/5694960/1888898

相关文章