JavaScript实现简单的双向绑定
很多的前端框架都支持数据双向绑定了,最近正好在看双向绑定的实现,就用Javascript写了几个简单的例子。
几个例子中尝试使用了下面的方式实现双向绑定:
- 发布/订阅模式
- 属性劫持
- 脏数据检测
发布/订阅模式
实现数据双向绑定最直接的方式就是使用PubSub模式:
- 当model发生改变的时候,触发Model change事件,然后通过响应的事件处理函数更新界面
- 当界面更新的时候,触发UI change事件, 然后通过相应的事件处理函数更新Model,以及绑定在Model上的其他界面控件
根据这个思路,可以定义'ui-update-event'和'model-update-event'两个事件,然后针对Model和UI分别进行这两个事件订阅和发布。
UI更新
对于所有支持双向绑定的页面控件,当控件的“值”发生改变的时候,就触发'ui-update-event',然后通过事件处理函数更新Model,以及绑定在Model上的其他界面控件
处理控件“值”的改变,发布“ui-update-event”事件,(这里只处理包含“t-binding”属性的控件):
// keyup和change事件处理函数
function pageElementEventHandler(e) {
var target = e.target || e.srcElemnt;
var fullPropName = target.getAttribute('t-binding');
if(fullPropName && fullPropName !== '') {
Pubsub.publish('ui-update-event', fullPropName, target.value);
}
}
// 在页面上添加keyup和change的listener
if(document.addEventListener) {
document.addEventListener('keyup', pageElementEventHandler, false);
document.addEventListener('change', pageElementEventHandler, false);
} else {
document.attachEvent('onkeyup', pageElementEventHandler);
document.attachEvent('onchange', pageElementEventHandler);
}
另外,对所有包含“t-binding”属性的控件都订阅了“'model-update-event”,也就是当Model变化的时候会收到相应的通知:
// 订阅model-update-event事件, 根据Model对象的变化更新相关的UI
Pubsub.subscrib('model-update-event', function(fullPropName, propValue) {
var elements = document.querySelectorAll('[t-binding="' + fullPropName + '"]');
for(var i = 0, len =elements.length; i < len; i++){
var elementType = elements[i].tagName.toLowerCase();
if(elementType === 'input' || elementType === 'textarea' || elementType === 'select') {
elements[i].value = propValue;
} else {
elements[i].innerHTML = propValue;
}
}
});
Model更新
对于Model这一层,当Model发生改变的时候,会发布“model-update-event”:
// Model对象更新方法,更新对象的同时发布model-update-event事件
'updateModelData': function(propName, propValue) {
eval(this.modelName)[propName] =propValue;
Pubsub.publish('model-update-event', this.modelName + '.' + propName, propValue);
}
另外,Model订阅了“ui-update-event”,相应的界面改动会更新Model
// 订阅ui-update-event事件, 将UI的变化对应的更新Model对象
Pubsub.subscrib('ui-update-event', function(fullPropName, propValue) {
var propPathArr = fullPropName.split('.');
self.updateModelData(propPathArr[1], propValue);
});
有了这些代码,一个简单的双向绑定例子就可以运行起来了:
初始状态

UI变化,Model会更新,绑定在Model上的其他控件也被更新

通过"updateModelData"更新Model,绑定在Model上的控件被更新

完整的代码请参考Two-way-data-binding:PubSub。
属性劫持
在“发布/订阅模式”实现双向绑定的例子中,为了保证Model的更新能够发布“model-update-event”,对于Model对象的改变必须通过“updateModelData”方法。
也就是说,通过Javascript对象字面量直接更新对象就没有办法触发双向绑定。
Javascript中提供了“Object.defineProperty”方法,通过这个方法可以对对象的属性进行定制。
结合“Object.defineProperty”和“发布/订阅模式”,对Model属性的set方法进行重定义,将“model-update-event”事件的发布直接放在Model属性的setter中:
'defineObjProp': function(obj, propName, propValue) {
var self = this;
var _value = propValue || '';
try {
Object.defineProperty(obj, propName, {
get: function() {
return _value;
},
// 在对象属性的setter中添加model-update-event事件发布动作
set: function(newValue) {
_value = newValue;
Pubsub.publish('model-update-event', self.modelName + '.' + propName, newValue);
},
enumerable: true,
configurable: true
});
obj[propName] = _value;
} catch (error) {
alert("Browser must be IE8+ !");
}
}
这样,就可以使用对象字面量的方式直接对Model对象进行修改:

但是,对于IE8及以下浏览器仍需要使用其它方法来做hack。
完整的代码请参考Two-way-data-binding:Hijacking。
脏数据检测
对于AngularJS,是通过脏数据检测来实现双向绑定的,下面就仿照脏数据检测来实现一个简单的双向绑定。
在这个例子中,作用域scope对象中会维护一个“watcher”数组,用来存放所以需要检测的表达式,以及对应的回调处理函数。
对于所有需要检测的对象、属性,scope通过“watch”方法添加到“watcher”数组中:
Scope.prototype.watch = function(watchExp, callback) {
this.watchers.push({
watchExp: watchExp,
callback: callback || function() {}
});
}
当Model对象发生变化的时候,调用“digest”方法进行脏检测,如果发现脏数据,就调用对应的回调函数进行界面的更新:
Scope.prototype.digest = function() {
var dirty;
do {
dirty = false;
for(var i = 0; i < this.watchers.length; i++) {
var newVal = this.watchers[i].watchExp(),
oldVal = this.watchers[i].last;
if(newVal !== oldVal) {
this.watchers[i].callback(newVal, oldVal);
dirty = true;
this.watchers[i].last = newVal;
}
}
} while(dirty);
}
完整的代码请参考Two-way-data-binding:Digest。
总结
到此,三个例子就介绍完了,例子很简单,希望对理解双向绑定的实现有一些帮助。
JavaScript实现简单的双向绑定的更多相关文章
- 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个最简单的双向绑定,原生十行代码让你看懂原理
废话不多说直接看效果图 代码很好理解,但是在看代码之前需要知道Vue双向绑定的原理其实就是基于Object.defineProperty 实现的双向绑定 官方传送门 这里我们用官方的话来说Object ...
- JavaScript实现数据的双向绑定
接触到Angulr.js和Vue.js后,提到最多的就是双向绑定 下面将用JavaScript实现数据的双向绑定 <!DOCTYPE html> <html> <head ...
- JavaScript实现简单的双向数据绑定
什么是双向数据绑定 双向数据绑定简单来说就是UI视图(View)与数据(Model)相互绑定在一起,当数据改变之后相应的UI视图也同步改变.反之,当UI视图改变之后相应的数据也同步改变. 双向数据绑定 ...
- 用jquery实现的简单数据双向绑定
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 用原生 JS 实现双向绑定及应用实例
写在前面: 所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更也能实时展现到界面.angular封装了双向绑定的方法,使双向绑定变得十分简单.但是在有些场景下(比如下面那个场景),不能使用 ...
- javascript中的双向绑定
阅读目录 一:发布订阅模式实现数据双向绑定 二:使用Object.defineProperty 来实现简单的双向绑定. 前言: 双向数据绑定的含义:可以将对象的属性绑定到UI,具体的说,我们有一个对象 ...
- vue2.0 双向绑定原理分析及简单实现
Vue用了有一段时间了,每当有人问到Vue双向绑定是怎么回事的时候,总是不能给大家解释的很清楚,正好最近有时间把它梳理一下,让自己理解的更清楚,下次有人问我的时候,可以侃侃而谈. 一.首先介绍Obje ...
- Vue2.0源码阅读笔记--双向绑定实现原理
上一篇 文章 了解了Vue.js的生命周期.这篇分析Observe Data过程,了解Vue.js的双向数据绑定实现原理. 一.实现双向绑定的做法 前端MVVM最令人激动的就是双向绑定机制了,实现双向 ...
- vue的双向绑定原理及实现
前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...
随机推荐
- (转)T-SQL查询处理执行顺序(一)
对于T-SQL编程,用得最广泛的,莫过于查询(Querying).要想写出高质量.高性能的查询语句,必须深入地了解逻辑查询处理. 一.逻辑查询处理的各个阶段 (5)SELECT DISTINCT TO ...
- Rational Rose :从用例图开始
前置条件:安装Rational Rose 2003 找开Rose工具,选择用例视图 Use Case View 先看看这个视图下面都有哪些工具,都能做一些什么: 下面详细说一下: 用例视图下面有工具 ...
- JavaScript for循环 闭包 【转】
个网友问了个问题,如下的html,为什么每次输出都是5,而不是点击每个p,就alert出对应的1,2,3,4,5. <html > <head> <meta http-e ...
- #8.12.16总结#background transition、animation、transform
background-origin 设置元素背景图片的原始起始位置.必须保证背景是background-repeat为no-repeat此属性才会生效. background-origin :bord ...
- MediaWiki使用指南
MediaWiki使用指南 MediaWiki是著名的开源wiki引擎,全球最大的wiki项目维基百科(百科词条协作系统)是使用MediaWiki的成功范例,MediaWiki的最大作用在于对知识的归 ...
- Swift安装
Server1 .Update sudo apt-get update sudo apt-get upgrade . sudo apt-get install bridge-utils .IP 3.1 ...
- SharePoint 2013 版本号和相关问题介绍
今天查SharePoint 补丁,无意间发现一个非常好的链接,分享给大家! 这里面有SharePoint近期的版本号,而且不断更新,还有每个补丁可能带来的问题,对于服务器经常需要打补丁的那是非常有用, ...
- 为Autodesk Viewer添加自定义工具条
如果你参加过我们近期的活动,你就会频繁的听到我们现在正在做的Autodesk Viewer大模型浏览器,这是一个不需要下载任何插件,基于WebGL技术的浏览器,可以支持几十种数据格式.同时viewer ...
- oracle中Window和Window Group
参考文献: http://www.5ienet.com/note/html/scheduler/oracle-scheduler-using-window.shtml window概念 此Window ...
- 遍历collection
链接 遍历NSArray 正向遍历 for (id object in array) 反向遍历 for (id object in [array reverseObjectEnumerator]) 如 ...