Object.observe API概述

最近,JavaScript的MVC框架在Web开发届非常盛行。在实现MVC框架的时候,一个非常重要的技术就是数据绑定技术。如果要实现模型与视图的分离,就必须要使用数据绑定技术。但是,MVC框架的原作者对于数据绑定处理实现得并不如人意,因此,Google公司在ECMAScript中封装了一个Object.observe API,专用于实现数据绑定处理(目前将其正式使用在V8中)。

Object.observe API可以被称为一种“可以对任何对象的属性值修改进行监视的事件处理函数”。

在Firefox浏览器中,实现了与之相类似的可以对DOM对象进行观察的Mutation观察器。

目前为止,Object.observe API已经被strawman proposal所承认,被正式使用在V8中。自11月末开始,已经可以在Chrome Canary与开发者通道中对其进行启用。

本文介绍Object.observe API中的基本功能及一些代码示例。

目前为止,Object.observe API中包括如下所示的四个方法:

  • Object.observe:为对象指定监视时调用的回调函数
  • Object.unobserve:移除监视时调用的回调函数
  • Object.deliverChangeRecords:通过回调函数对对象值进行修改
  • Object.getNotifier:获取Notifier对象

可以观察到的属性操作包括以下几种:

  • new:添加属性
  • updated:修改属性值
  • reconfigured:修改属性设定
  • deleted:删除属性

接下来介绍如何使用Object.observe方法。

目前(2012年12月6日)为止,如果要使用Object.observe API,需要使用Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器,同时在chrome://flags/中启用“启用实验性 JavaScript”选项,如下图所示。


简单代码示例

Object.observe方法用于为对象指定监视到属性修改时调用的回调函数,使用方法如下所示。

Object.observe(obj, callback);

Object.observe方法中使用两个参数,其中第一个参数值为需要被监视的对象,第二个参数值为监视到属性修改时调用的回调函数名。可以将各对象的属性操作时生成的ChangeRecord对象数组设置为回调函数的参数。

ChangeRecord对象拥有type、name、oldValue、object四个属性,各属性含义如下所示。

function callback(changes) {changes.forEach(function(change) {console.log(change.type);     //对属性进行了什么操作 new/updated/reconfigured/deltedconsole.log(change.name);     //属性名console.log(change.oldValue); //修改之前的属性值console.log(change.object);   //被监视的对象});
}

使用如下所示的代码,可以在任何时刻对于对象属性的上述四种操作(new/updated/reconfigured/delted)进行监视:

var obj = {a: 1};Object.observe(obj, output); //为对象指定监视时调用的回调函数obj.b = 2; //添加属性obj.a = 2; //修改属性值Object.defineProperties(obj, {a: { enumerable: false}}); //修改属性设定delete obj.b; //删除属性  function output(change) {//回调函数,可以在此处书写在页面上的输出。}

<!DOCTYPE html> <head> <meta charset="UTF-8" /> <title>Object.observer API代码示例页面</title> <style> table, td, th { border: 2px #000000 solid; } </style> <script> window.addEventListener('DOMContentLoaded',function() { if (!Object.observe) { alert('您的浏览器不支持Object.observe API。请使用Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器,并启用“启用实验性 JavaScript”选项。'); return; } var obj = {a: 1}; Object.observe(obj, output); obj.b = 2; //添加属性 obj.a = 2; //修改属性值 Object.defineProperties(obj, {a: { enumerable: false}}); //修改属性设定 delete obj.b; //删除属性 function output(changes) { var results = document.getElementById('results'); var table = document.createElement('table'); results.appendChild(table); var caption = document.createElement('caption'); caption.innerText = '监视到的事件列表'; table.appendChild(caption); var thead = document.createElement('thead'); thead.innerHTML = '<tr><th>序号</th><th>操作种类</th><th>属性名</th><th>修改前的属性值</th><th>修改后的属性值</th></tr>'; table.appendChild(thead); changes.forEach(function(change, i) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + i + '</td><td>' + change.type + '</td><td>' + change.name + '</td><td>' + change.oldValue + '</td><td>' + change.object[change.name] + '</td>'; table.appendChild(tr); }); } }); </script> </head> <body> <div id="event"><div>示例代码</div> <pre> obj.b = 2; //添加属性 obj.a = 2; //修改属性值 Object.defineProperties(obj, {a: { enumerable: false}}); //修改属性设定 delete obj.b; //删除属性 </pre> </div> <div id="results"></div> </body> </html>

运行代码

页面运行结果如下图所示(在Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器中)


创建自定义Notify对象

可以监视到的事件并不局限于以上所述的几种,可以自定义监视事件。

可以使用Notifier对象来自定义针对对象的可访问属性(可使用getter方法或setter方法读取或设置的属性)被修改时所触发的事件。这时,我们需要Object.getNotifier()方法获取被监视对象的Notifier对象,并使用notify方法进行属性被修改的通知。

在以下这个示例代码中,为对象自定义time_updated事件及time_read事件并利用这两个事件监视对象的私有属性_time的读取及修改。

var obj2 = {_time: new Date(0)};
var notifier = Object.getNotifier(obj2); //获取Notifier对象
Object.defineProperties(obj2, { //设置对象的可访问属性 _time: {enumerable: false,configrable: false},seen: {set: function(val) {var notifier = Object.getNotifier(this);notifier.notify({type: 'time_updated', //定义time_updated事件name: 'seen',oldValue: this._time});this._time = val;},get: function() {var notifier = Object.getNotifier(this);notifier.notify({type: 'time_read', //定义time_read事件name: 'seen',oldValue: this._time});return this._time;}}
});
Object.observe(obj2, output); //为对象指定监视时调用的回调函数
//执行属性操作
var first_time = obj2.seen; //触发time_read事件
obj2.seen = new Date();      //触发time_updated事件
var second_time = obj2.seen; //触发time_read事件

<!DOCTYPE html> <head> <title>Object.observer API代码示例页面</title> <style> table, td, th { border: 2px #000000 solid; } </style> <script> window.addEventListener('DOMContentLoaded',function() { if (!Object.observe) { alert('您的浏览器不支持Object.observe API。请使用Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器,并启用“启用实验性 JavaScript”选项。'); return; } var obj2 = {_time: new Date(0)}; var notifier = Object.getNotifier(obj2); Object.defineProperties(obj2, { _time: { enumerable: false, configrable: false }, seen: { set: function(val) { var notifier = Object.getNotifier(this); notifier.notify({ type: 'time_updated', //定义time_updated事件 name: 'seen', oldValue: this._time }); this._time = val; }, get: function() { var notifier = Object.getNotifier(this); notifier.notify({ type: 'time_read', //定义time_read事件 name: 'seen', oldValue: this._time }); return this._time; } } }); Object.observe(obj2, output); var seen = document.getElementById('seen'); var first_seen = document.createElement('div'); var first_time = obj2.seen; //触发time_read事件 first_seen.innerText = 'first_seen:' + first_time; seen.appendChild(first_seen); obj2.seen = new Date(); //触发time_updated事件 var second_seen = document.createElement('div'); var second_time = obj2.seen; //触发time_read事件 second_seen.innerText = 'second_seen:' + second_time; seen.appendChild(second_seen); function output(changes) { var results = document.getElementById('results'); var table = document.createElement('table'); results.appendChild(table); var caption = document.createElement('caption'); caption.innerText = '监视到的事件列表'; table.appendChild(caption); var thead = document.createElement('thead'); thead.innerHTML = '<tr><th>序号</th><th>操作种类</th><th>属性名</th><th>修改前的属性值</th></tr>'; table.appendChild(thead); changes.forEach(function(change, i) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + i + '</td><td>' + change.type + '</td><td>' + change.name + '</td><td>' + change.oldValue + '</td>'; table.appendChild(tr); }); } }); </script> </head> <body> <div id="event"><div>示例代码</div> <pre> var first_time = obj2.seen; //触发time_read事件 obj2.seen = new Date(); //触发time_updated事件 var second_time = obj2.seen; //触发time_read事件 </pre> </div> <div id="seen"></div> <div id="results"></div> </body> </html>

运行代码


控制回调函数的执行时间

在默认情况下,使用Object.observe API指定的回调函数将在JavaScript脚本代码执行结束时被调用。因此如果对同一对象的同一属性执行了多次操作,回调函数中获取到的各属性值为最后一个操作结束后的值。将前面这个示例中的代码稍作修改,对使用Object.observe API进行监视的对象的属性值连续修改七次(为了避免回调函数的循环调用删除对time_read事件的监视)。

obj3.seen = new Date(2013, 0, 1, 0, 0, 0); //触发time_updated事件obj3.seen = new Date(2013, 0, 2, 0, 0, 0); //触发time_updated事件obj3.seen = new Date(2013, 0, 3, 0, 0, 0); //触发time_updated事件obj3.seen = new Date(2013, 0, 4, 0, 0, 0); //触发time_updated事件obj3.seen = new Date(2013, 0, 5, 0, 0, 0); //触发time_updated事件obj3.seen = new Date(2013, 0, 6, 0, 0, 0); //触发time_updated事件obj3.seen = new Date(2013, 0, 7, 0, 0, 0); //触发time_updated事件

<!DOCTYPE html> <head> <title>Object.observer API代码示例页面</title> <style> table, td, th { border: 2px #000000 solid; } </style> <script> window.addEventListener('DOMContentLoaded',function() { if (!Object.observe) { alert('您的浏览器不支持Object.observe API。请使用Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器,并启用“启用实验性 JavaScript”选项。'); return; } var obj3 = {_time: new Date(0)}; var notifier = Object.getNotifier(obj3); Object.defineProperties(obj3, { _time: { enumerable: false, configrable: false }, seen: { set: function(val) { var notifier = Object.getNotifier(this); notifier.notify({ type: 'time_updated', // 時間更新イベントの定義 name: 'seen', oldValue: this._time }); this._time = val; }, get: function() { return this._time; } } }); Object.observe(obj3, output); obj3.seen = new Date(2013, 0, 1, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 2, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 3, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 4, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 5, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 6, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 7, 0, 0, 0); //触发time_updated事件 function output (changes) { var results = document.getElementById('results'); var table = document.createElement('table'); results.appendChild(table); var caption = document.createElement('caption'); caption.innerText = '监视到的事件列表'; table.appendChild(caption); var thead = document.createElement('thead'); thead.innerHTML = '<tr><th>序号</th><th>操作种类</th><th>属性名</th><th>修改前的属性值</th><th>修改后的属性值</th></tr>'; table.appendChild(thead); changes.forEach(function(change, i) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + i + '</td><td>' + change.type + '</td><td>' + change.name + '</td><td>' + change.oldValue + '</td><td>' + change.object[change.name] + '</td>'; table.appendChild(tr); }); } }); </script> </head> <body> <div id="event"><div>示例代码</div> <pre> obj3.seen = new Date(2013, 0, 1, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 2, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 3, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 4, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 5, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 6, 0, 0, 0); //触发time_updated事件 obj3.seen = new Date(2013, 0, 7, 0, 0, 0); //触发time_updated事件 </pre> </div> <div id="seen"></div> <div id="results"></div> </body> </html>

运行代码

页面运行结果如下图所示(在Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器中)


从这个结果中我们可以看出,在回调函数中获取到的属性值为同一个属性值(2013年1月7日)。为了强制获取事件触发后立即设置的属性值,我们需要使用Object.deliverChangeRecords方法。

在如下所示的代码中,每次修改了属性值后,即调用Object.deliverChangeRecords方法立即调用回调函数。

obj4.seen = new Date(2013, 0, 1, 0, 0, 0); //触发time_updated事件Object.deliverChangeRecords(output);       //调用回调函数obj4.seen = new Date(2013, 0, 2, 0, 0, 0); //触发time_updated事件Object.deliverChangeRecords(output);       //调用回调函数obj4.seen = new Date(2013, 0, 3, 0, 0, 0); //触发time_updated事件Object.deliverChangeRecords(output);       //调用回调函数obj4.seen = new Date(2013, 0, 4, 0, 0, 0); //触发time_updated事件Object.deliverChangeRecords(output);       //调用回调函数obj4.seen = new Date(2013, 0, 5, 0, 0, 0); //触发time_updated事件Object.deliverChangeRecords(output);       //调用回调函数obj4.seen = new Date(2013, 0, 6, 0, 0, 0); //触发time_updated事件Object.deliverChangeRecords(output);       //调用回调函数obj4.seen = new Date(2013, 0, 7, 0, 0, 0); //触发time_updated事件

<!DOCTYPE html> <head> <title>Object.observer API代码示例页面</title> <style> table, td, th { border: 2px #000000 solid; } </style> <script> window.addEventListener('DOMContentLoaded',function() { if (!Object.observe) { alert('您的浏览器不支持Object.observe API。请使用Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器,并启用“启用实验性 JavaScript”选项。'); return; } var obj4 = {_time: new Date(0)}; var notifier = Object.getNotifier(obj4); Object.defineProperties(obj4, { _time: { enumerable: false, configrable: false }, seen: { set: function(val) { var notifier = Object.getNotifier(this); notifier.notify({ type: 'time_updated', // 時間更新イベントの定義 name: 'seen', oldValue: this._time }); this._time = val; }, get: function() { return this._time; } } }); Object.observe(obj4, output); obj4.seen = new Date(2013, 0, 1, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 2, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 3, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 4, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 5, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 6, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 7, 0, 0, 0); //触发time_updated事件 function output (changes) { var results = document.getElementById('results'); var table = document.createElement('table'); results.appendChild(table); var caption = document.createElement('caption'); caption.innerText = '监视到的事件列表'; table.appendChild(caption); var thead = document.createElement('thead'); thead.innerHTML = '<tr><th>序号</th><th>操作种类</th><th>属性名</th><th>修改前的属性值</th><th>修改后的属性值</th></tr>'; table.appendChild(thead); changes.forEach(function(change, i) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + i + '</td><td>' + change.type + '</td><td>' + change.name + '</td><td>' + change.oldValue + '</td><td>' + change.object[change.name] + '</td>'; table.appendChild(tr); }); } }); </script> </head> <body> <div id="event"><div>示例代码</div> <pre> obj4.seen = new Date(2013, 0, 1, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 2, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 3, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 4, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 5, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 6, 0, 0, 0); //触发time_updated事件 Object.deliverChangeRecords(output); //调用回调函数 obj4.seen = new Date(2013, 0, 7, 0, 0, 0); //触发time_updated事件 </pre> </div> <div id="seen"></div> <div id="results"></div> </body> </html>

运行代码

页面运行结果如下图所示(在Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的浏览器中)


从这个结果中我们可以看出,每次执行Object.deliverChangeRecords方法时都将调用回调函数在页面中输出修改后的属性值。

使用Object.observe 实现数据绑定相关推荐

  1. 【JavaScript】Object.observe()带来的数据绑定变革

    Object.observe()带来的数据绑定变革 引言 一场变革即将到来.一项Javascript中的新特性将会改变你对于数据绑定的所有认识.它也将改变你所使用的MVC库观察模型中发生的修改以及更新 ...

  2. Object.observe() js新特性

    Object.observe() 用来实时监测js中对象的变化,变化时调用一个方法.使用此方法,可以代替angular中的脏检查,可以大大的提高性能,详情可以看下文. [翻译]Object.obser ...

  3. Object.observe将不加入到ES7

    先请看 Object.observe 的 API Object.observe(obj, callback[, acceptList]) 它用来监听对象的变化,当给该对象添加属性,修改属性时都会被依次 ...

  4. vue 数据绑定实现的核心 Object.defineProperty()

    vue深入响应式原理 现在是时候深入一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的 JavaScript 对象.而当你修改它们时,视图会进行更新.这使得状态管理非常简 ...

  5. html或原生js是单一对应绑定的,原生js数据绑定

    双向数据绑定是非常重要的特性 -- 将JS模型与HTML视图对应,能减少模板编译时间同时提高用户体验.我们将学习在不使用框架的情况下,使用原生JS实现双向绑定 -- 一种为Object.observe ...

  6. 理解$watch ,$apply 和 $digest --- 理解数据绑定过程

    原文地址:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ 注 这篇博文主要是写给新手的,是给那些刚刚开始 ...

  7. 双向数据绑定原理(三种实现方式)

    <!DOCTYPE html> <html><head><meta charset="UTF-8"><title>双向数 ...

  8. JavaScript实现简单的双向数据绑定(Ember、Angular、Vue)

    tip:有问题或者需要大厂内推的+我脉脉哦:丛培森 ٩( 'ω' )و [本文源址:http://blog.csdn.net/q1056843325/article/details/72999948 ...

  9. 神奇的 Object.defineProperty 解释说明

    原文 : https://segmentfault.com/a/1190000004346467?utm_source=tuicool&utm_medium=referral 这个方法了不起啊 ...

最新文章

  1. Element 2.6.0 发布,基于 Vue 2.0 的桌面端组件库
  2. 详解vue 路由跳转四种方式 (带参数)
  3. wamp安装多版本php,WampServer下安装多个版本的PHP、mysql、apache图文教程
  4. 案例:无人测量船水库水下地形测量及库容量计算
  5. React 篇 Search Bar and content Table
  6. 26 | 红黑树(下):掌握这些技巧,你也可以实现一个红黑树
  7. js 动态创建注释节点 createComment
  8. 论文 | 多传感器数据深度图的融合:最近基于深度学习的方法(上)
  9. gtp文件服务器,GTP中文网吉它谱吉他谱guitar网站
  10. mac osx vi 设置tab 四个空格
  11. MindManager中读图工具的使用
  12. QQ for linux(ubuntu) 下载安装教程
  13. STM32 BOOT模式设置
  14. C++ STL库之vector
  15. IT项目画原型图工具介绍
  16. 银联支付(chinapay)java接入避坑指南
  17. 威斯敏斯特教堂 名言_新教堂徽标设计师的视角分析
  18. 最小二乘法及应用实例
  19. 受用一生的高效 PyCharm 使用技巧
  20. Matlab入门基础详解

热门文章

  1. Solaris10下Nagios安装
  2. 解决 CentOS7 容器 Failed to get D-Bus connection: Operation not permitted
  3. JAVA Socket实现多客户端聊天
  4. layui2.4.0前的table隐藏列
  5. BZOJ 1051: [HAOI2006]受欢迎的牛
  6. javascript面向对象属性函数用法(defineProperty与getOwnPropertyDescriptor)
  7. [转]数据库分库分表
  8. linux命令crontab
  9. linux apache添加多站点配置(Ubuntn和Centos)
  10. Monkey测试com.android.browser 发 生类似android.datab