We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
随着前端交互复杂度的提升,各类框架如angular,react,vue等也层出不穷,这些框架一个比较重要的技术点就是数据绑定。数据的监听有较多的实现方案,本文将粗略的描述一番,并对其中一个兼容性较好的深入分析。 #实现方案简介 目前对象的监听可行的方案:
ES5现代浏览器基本都支持了,OK,本文将介绍目前支持度最好的object.defineproperty 的Setters 和 Getters方式
它属于es5规范,有两种定义属性:
数据属性的例子
obj.key='static'; //等效于 Object.defineProperty(obj, "key", { enumerable: true, configurable: true, writable: true, value: "static" });
访问器属性例子
var obj = { temperature:'test' }; var temperature=''; Object.defineProperty(obj, 'temperature', { get: function() { return temperature+'-----after'; }, set: function(value) { temperature = value; } }) obj.temperature='Test'; //Test-----after console.log(obj.temperature);
火狐开发者
比较复杂的是数组的封装,结构如下: 新建一个对象newProto,继承Array的原型,并在newProto上面封装push,pop等数组操作方法,再将传入的array对象的原型设置为newProto。
##路径的定位 在获取数据变化的同时,定位该变化数据在原始根对象的位置,以数组表示如: 如[ 'a', 'dd', 'ddd' ] 表示对象obj.a.dd.ddd的属性改变 实现:每个遍历对象属性都通过path.slice(0)的方式复制入参数组path,生成新数组tpath,给tpath数组push对应的对象属性key,最后在执行set的回调函数时候将tpath当参数传入
watch.js
/** * * @param obj 需要监听的对象或数组 * @param callback 当对应属性变化的时候触发的回调函数 * @constructor */ function Watch(obj, callback) { this.callback = callback; //监听_obj对象 判断是否为对象,如果是数组,则对数组对应的原型进行封装 //path代表相应属性在原始对象的位置,以数组表示. 如[ 'a', 'dd', 'ddd' ] 表示对象obj.a.dd.ddd的属性改变 this.observe = function (_obj, path) { var type=Object.prototype.toString.call(_obj); if (type== '[object Object]'||type== '[object Array]') { this.observeObj(_obj, path); if (type == '[object Array]') { this.cloneArray(_obj, path); } } }; //遍历对象obj,设置set,get属性,set属性能触发callback函数,并将val的值改为newVal //遍历结束后再次调用observe函数 判断val是否为对象,如果是则在对val进行遍历设置set,get this.observeObj = function (obj, path) { var t = this; Object.keys(obj).forEach(function (prop) { var val = obj[prop]; var tpath = path.slice(0); tpath.push(prop); Object.defineProperty(obj, prop, { get: function () { return val; }, set: function (newVal) { t.callback(tpath, newVal, val); val = newVal; } }); t.observe(val, tpath); }); }; //通过对特定数组的原型中间放一个newProto原型,该原型继承于Array的原型,但是对push,pop等数组操作属性进行封装 this.cloneArray = function (a_array, path) { var ORP = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']; var arrayProto = Array.prototype; var newProto = Object.create(arrayProto); var t = this; ORP.forEach(function (prop) { Object.defineProperty(newProto, prop, { value: function (newVal) { path.push(prop); t.callback(path, newVal); arrayProto[prop].apply(a_array, arguments); }, enumerable: false, configurable: true, writable: true }); }); a_array.__proto__ = newProto; }; //开始监听obj对象,初始path为[] this.observe(obj, []); }
index.html
<body> <ul> <li> <a href="javascript:void(0)" onClick="dataOne()"> 将obj b属性改变 </a> </li> <li> <a href="javascript:void(0)" onClick="dataTwo()"> 将obj a属性的dd属性的ddd属性改变 </a> </li> <li> <a href="javascript:void(0)" onClick="dataThree()"> 将obj a属性的g属性数组第一个值的a属性改变 </a> </li> <li> <a href="javascript:void(0)" onClick="dataFour()"> 将obj a属性的g属性数组push新的值 </a> </li> </ul> <div id="path"> </div> <div id="old-val"> </div> <div id="new-val"> </div> </body> <script src="../src/watch.js"></script> <script> var obj = { a: {e: 4, f: 5, g: [{a: 1, b: 2}, [3, 4]], dd: {ddd: 1}}, b: 2, c: 3 }; new Watch(obj, call); function call(path, newVal, oldVal) { document.getElementById('path').innerHTML='路径:'+path; document.getElementById('old-val').innerHTML='新的值:'+newVal; document.getElementById('new-val').innerHTML='老的值:'+oldVal; } function dataOne() { obj.b = Math.floor(Math.random()*10); } function dataTwo() { obj.a.dd.ddd = Math.floor(Math.random()*10); } function dataThree() { obj.a.g[0].a=Math.floor(Math.random()*10); } function dataFour() { obj.a.g.push(Math.floor(Math.random()*10)); } </script>
#代码地址 完整代码地址
具体流程的复杂度基于监听对象的深度,所以下图只对父对象做流程分析
本文有什么不完善的地方,或者流程图有待改进的地方,敬请斧正。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
随着前端交互复杂度的提升,各类框架如angular,react,vue等也层出不穷,这些框架一个比较重要的技术点就是数据绑定。数据的监听有较多的实现方案,本文将粗略的描述一番,并对其中一个兼容性较好的深入分析。
#实现方案简介
目前对象的监听可行的方案:
ES5现代浏览器基本都支持了,OK,本文将介绍目前支持度最好的object.defineproperty 的Setters 和 Getters方式
object.defineproperty介绍
简洁的介绍
它属于es5规范,有两种定义属性:
数据属性的例子
访问器属性例子
详细的介绍
火狐开发者
实现监听的思路
数组的封装
比较复杂的是数组的封装,结构如下:
新建一个对象newProto,继承Array的原型,并在newProto上面封装push,pop等数组操作方法,再将传入的array对象的原型设置为newProto。
对应图
##路径的定位
在获取数据变化的同时,定位该变化数据在原始根对象的位置,以数组表示如:
如[ 'a', 'dd', 'ddd' ] 表示对象obj.a.dd.ddd的属性改变
实现:每个遍历对象属性都通过path.slice(0)的方式复制入参数组path,生成新数组tpath,给tpath数组push对应的对象属性key,最后在执行set的回调函数时候将tpath当参数传入
带注释代码
watch.js
index.html
效果图
#代码地址
完整代码地址
流程图
具体流程的复杂度基于监听对象的深度,所以下图只对父对象做流程分析

归纳
最后
本文有什么不完善的地方,或者流程图有待改进的地方,敬请斧正。
The text was updated successfully, but these errors were encountered: