很多的前端框架都支持数据双向绑定了,最近正好在看双向绑定的实现,就用Javascript写了几个简单的例子。

几个例子中尝试使用了下面的方式实现双向绑定:

  1. 发布/订阅模式
  2. 属性劫持
  3. 脏数据检测

发布/订阅模式

实现数据双向绑定最直接的方式就是使用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);
});

有了这些代码,一个简单的双向绑定例子就可以运行起来了:

  1. 初始状态

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

  3. 通过"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实现简单的双向绑定的更多相关文章

  1. 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个最简单的双向绑定,原生十行代码让你看懂原理

    废话不多说直接看效果图 代码很好理解,但是在看代码之前需要知道Vue双向绑定的原理其实就是基于Object.defineProperty 实现的双向绑定 官方传送门 这里我们用官方的话来说Object ...

  2. JavaScript实现数据的双向绑定

    接触到Angulr.js和Vue.js后,提到最多的就是双向绑定 下面将用JavaScript实现数据的双向绑定 <!DOCTYPE html> <html> <head ...

  3. JavaScript实现简单的双向数据绑定

    什么是双向数据绑定 双向数据绑定简单来说就是UI视图(View)与数据(Model)相互绑定在一起,当数据改变之后相应的UI视图也同步改变.反之,当UI视图改变之后相应的数据也同步改变. 双向数据绑定 ...

  4. 用jquery实现的简单数据双向绑定

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 用原生 JS 实现双向绑定及应用实例

    写在前面: 所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更也能实时展现到界面.angular封装了双向绑定的方法,使双向绑定变得十分简单.但是在有些场景下(比如下面那个场景),不能使用 ...

  6. javascript中的双向绑定

    阅读目录 一:发布订阅模式实现数据双向绑定 二:使用Object.defineProperty 来实现简单的双向绑定. 前言: 双向数据绑定的含义:可以将对象的属性绑定到UI,具体的说,我们有一个对象 ...

  7. vue2.0 双向绑定原理分析及简单实现

    Vue用了有一段时间了,每当有人问到Vue双向绑定是怎么回事的时候,总是不能给大家解释的很清楚,正好最近有时间把它梳理一下,让自己理解的更清楚,下次有人问我的时候,可以侃侃而谈. 一.首先介绍Obje ...

  8. Vue2.0源码阅读笔记--双向绑定实现原理

    上一篇 文章 了解了Vue.js的生命周期.这篇分析Observe Data过程,了解Vue.js的双向数据绑定实现原理. 一.实现双向绑定的做法 前端MVVM最令人激动的就是双向绑定机制了,实现双向 ...

  9. vue的双向绑定原理及实现

    前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...

随机推荐

  1. MP4和HR-HDTV压制教程

    写在前 几年前还没工作的时候,长期混迹于百度“恐怖片吧”和“电锯惊魂吧”.因喜欢看电影,也自学了RMVB内嵌字幕的压制. 偶然机会加入@谢耳朵字幕组,因RMVB过于陈旧,人人影视所有美剧也全面抛弃了R ...

  2. Quill – 可以灵活自定义的开源的富文本编辑器

    Quill 的建立是为了解决现有的所见即所得(WYSIWYG)的编辑器本身就是所见即所得(指不能再扩张)的问题.如果编辑器不正是你想要的方式,这是很难或不可能对其进行自定义以满足您的需求. Quill ...

  3. Maven发布工程到公共库

    1.发布工程 新建一个 Maven build 选择要发布的工程

  4. JavaScript学习09 函数本质及Function对象深入探索

    JavaScript学习09 函数本质及Function对象深入探索 在JavaScript中,函数function就是对象. JS中没有方法重载 在JavaScript中,没有方法(函数)重载的概念 ...

  5. JSON数据解析 基础知识及链接收集

    JSON数据解析学习 JSON介绍 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式. JSON 是存储和交换文本信息的语法.类似 XML.但是JSON 比 ...

  6. 操作系统开发系列—13.a.进程 ●

    进程的切换及调度等内容是和保护模式的相关技术紧密相连的,这些代码量可能并不多,但却至关重要. 我们需要一个数据结构记录一个进程的状态,在进程要被挂起的时候,进程信息就被写入这个数据结构,等到进程重新启 ...

  7. Xcode8 pod install 报错 “Generating Pods project Abort trap

    Xcode8 pod install 报错 "Generating Pods project Abort trap 今天在写一个新项目的时候,使用cocoapods在执行 $ pod ins ...

  8. objective-c系列-NSMutableArray

    ******************************************** // 可变数组构造方法 //  下边两句的定义都是不可变的 //    NSMutableArray *mar ...

  9. Eclipse下使用SVN版本控制

    作者:朱先忠编译 转自天极[url]http://dev.yesky.com/356/2578856.shtml[/url] 简单介绍一些基本操作1.同步在Eclipse下,右击你要同步的工程-tea ...

  10. Java Maps的9个常见问题

    一般来说,Map是一种由键值对组成的数据结构,其中键(key)在Map中是不能重复的: 本篇文章总结了9个最常见的问题(关于Java Map及其实现类): 出于简单考虑,在代码例子中我将不使用泛型,因 ...