1 /** 2 * observejs --- By dnt http://kmdjs.github.io/ 3 * Github: https://github.com/kmdjs/observejs 4 * MIT Licensed. 5 * Sorrow.X --- 添加注释,注释纯属个人理解 6 */ 7 ; (function(win) { 8 9 var observe = function(target, arr, callback) { 10 11 var _observe = function(target, arr, callback) { // target: 监听对象, arr: 监听对象的属性列表, callback: 回调函数 12 if (!target.$observer) target.$observer = this; // 给target监听对象添加$observer属性,值为_observe构造函数的实例 13 var $observer = target.$observer; // 把实例赋值给$observer变量 14 var eventPropArr = []; //事件属性列表 15 if (observe.isArray(target)) { // 监听对象如果是数组 16 if (target.length === 0) { // 如果是个空数组 17 target.$observeProps = {}; // 给监听对象添加新属性$observeProps且赋值为{}空对象 18 target.$observeProps.$observerPath = '#'; // 给监听对象的属性$observeProps对象添加$observerPath属性且赋值为'#' 19 }; 20 $observer.mock(target); // 调用原型上的mock方法(给target加上数组的方法) 21 }; 22 for (var prop in target) { // 遍历监听对象属性(含原型上的属性) 23 if (target.hasOwnProperty(prop)) { // 只对对象自身的属性感兴趣(不要原型上的) 24 if (callback) { // 如果用户传了三个参数的话 25 if (observe.isArray(arr) && observe.isInArray(arr, prop)) { // arr如果是数组且prop属性在数组中 26 eventPropArr.push(prop); 27 $observer.watch(target, prop); 28 } else if (observe.isString(arr) && prop == arr) { // arr如果是字符串且prop属性与arr字符串一样 29 eventPropArr.push(prop); 30 $observer.watch(target, prop); 31 }; 32 } else { 33 eventPropArr.push(prop); //添加target的属性到eventPropArr数组 34 $observer.watch(target, prop); // 调用原型上的watch方法(给属性添加监听) 35 }; 36 }; 37 }; 38 $observer.target = target; // 给$observer对象添加target属性 39 if (!$observer.propertyChangedHandler) $observer.propertyChangedHandler = []; // 给$observer对象添加属性propertyChangedHandler,值为空数组 40 var propChanged = callback ? callback : arr; // propChanged存储回调函数 41 $observer.propertyChangedHandler.push({ // 给$observer对象或者target.$observer对象(其实就是this实例啦)属性propertyChangedHandler数组添加一个对象 42 all: !callback, 43 propChanged: propChanged, 44 eventPropArr: eventPropArr 45 }); 46 }; 47 48 _observe.prototype = { // 原型 49 50 "onPropertyChanged": function(prop, value, oldValue, target, path) { // prop: 属性, value: 设置的新值, oldValue: 上一次属性的值, target: 监听对象, path: 路径 51 if (value !== oldValue && this.propertyChangedHandler) { // prop的新旧值不同且实例的propertyChangedHandler属性为真值 52 var rootName = observe._getRootName(prop, path); 53 for (var i = 0, len = this.propertyChangedHandler.length; i < len; i++) { // 循环遍历 54 var handler = this.propertyChangedHandler[i]; // 数组成员 55 if (handler.all || observe.isInArray(handler.eventPropArr, rootName) || rootName.indexOf("Array-") === 0) { 56 handler.propChanged.call(this.target, prop, value, oldValue, path); // 执行用户的回调函数 57 }; 58 }; 59 }; 60 if (prop.indexOf('Array-') !== 0 && typeof value === 'object') { // prop字符串不包含'Array-'且value是array或者object 61 this.watch(target, prop, target.$observeProps.$observerPath); // 属性为对象或者数组,再次对其属性进行监听 62 }; 63 }, 64 65 "mock": function(target) { // target: 数组 66 var self = this; // 存个实例 67 observe.methods.forEach(function(item) { // 遍历数组的每个方法 68 target[item] = function() { // 给target数组对象添加方法 69 var old = Array.prototype.slice.call(this, 0); // 把原始数组存一下 70 var result = Array.prototype[item].apply(this, Array.prototype.slice.call(arguments)); // 数组不同方法的返回值 71 if (new RegExp("\b" + item + "\b").test(observe.triggerStr)) { 72 for (var cprop in this) { 73 if (this.hasOwnProperty(cprop) && !observe.isFunction(this[cprop])) { // 数组含有可枚举的属性并且属性不是函数 74 self.watch(this, cprop, this.$observeProps.$observerPath); // 对数组的属性进行监听(以前的属性也重新再次监听了,我觉得不太好,可以改进,不知道理解错了没) 75 }; 76 }; 77 // todo 78 self.onPropertyChanged("Array-" + item, this, old, this, this.$observeProps.$observerPath); 79 }; 80 return result; 81 }; 82 target['real' + item.substring(0, 1).toUpperCase() + item.substring(1)] = function() { // 返回数组方法真实的的结果(其实就是调用数组的方法) 83 return Array.prototype[item].apply(this, Array.prototype.slice.call(arguments)); 84 }; 85 }); 86 }, 87 88 "watch": function(target, prop, path) { // target: 监听对象, prop: 监听对象的属性, path: 调用路径 89 if (prop === "$observeProps" || prop === "$observer") return; // 如果监听对象的属性等于$observeProps, $observer这2个属性中的任何一个,就return掉了 90 if (observe.isFunction(target[prop])) return; // 如果监听对象的属性类型是函数,也return掉 91 if (!target.$observeProps) target.$observeProps = {}; // 如果target对象没有$observeProps属性,则加上这个属性且值为{}, 有的话就跳过 92 if (path !== undefined) { // 如果路径不为空,则设置$observerPath值为path 93 target.$observeProps.$observerPath = path; 94 } else { // 否则默认'#' 95 target.$observeProps.$observerPath = '#'; 96 }; 97 var self = this; // self存个_observe实例对象 98 var currentValue = target.$observeProps[prop] = target[prop]; // 当前属性的value值(target.$observeProps对象添加了这个prop属性且有值) 99 Object.defineProperty(target, prop, { // 给target对象的属性添加set, get方法 100 get: function() { // 返回 target.$observeProps属性的值 101 return this.$observeProps[prop]; 102 }, 103 set: function(value) { 104 var old = this.$observeProps[prop]; // 存一下上一次的属性值 105 this.$observeProps[prop] = value; // 设置新的属性值 106 self.onPropertyChanged(prop, value, old, this, target.$observeProps.$observerPath); // 设置值时,触发实例onPropertyChanged方法 107 } 108 }); 109 if (typeof currentValue == 'object') { // 如果属性值是array或者object 110 if (observe.isArray(currentValue)) { // 如果是数组 111 this.mock(currentValue); // 调用原型上的mock方法 112 if (currentValue.length === 0) { // 如果是空数组 113 if (!currentValue.$observeProps) currentValue.$observeProps = {}; // 如果currentValue对象没有$observeProps属性,则加上这个属性且值为{}, 有的话就跳过 114 if (path !== undefined) { // 如果路径不为空,则设置currentValue.$observerPath值为path 115 currentValue.$observeProps.$observerPath = path; 116 } else { // 否则默认'#' 117 currentValue.$observeProps.$observerPath = '#'; 118 }; 119 }; 120 }; 121 for (var cprop in currentValue) { // 循环currentValue对象的每一个成员 122 if (currentValue.hasOwnProperty(cprop)) { // 只对对象自身的属性感兴趣(不要原型上的) 123 this.watch(currentValue, cprop, target.$observeProps.$observerPath + '-' + prop); // 再次监听(递归) 124 }; 125 }; 126 }; 127 } 128 }; 129 130 return new _observe(target, arr, callback); // 实例化_observe构造函数 131 }; 132 133 // observe的静态属性 134 observe.methods = ["concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toString", "unshift", "values", "size"]; 135 observe.triggerStr = ["concat", "copyWithin", "fill", "pop", "push", "reverse", "shift", "sort", "splice", "unshift", "size"].join(","); 136 137 // observe的静态方法(工具函数) 138 observe.isArray = function(arr) { // 判断参数arr是否为数组 139 return Object.prototype.toString.call(arr) === '[object Array]'; 140 }; 141 142 observe.isInArray = function(arr, item) { // 判断item是否在数组arr中 143 for (var i = arr.length; --i > -1; ) { 144 if (item === arr[i]) return true; 145 }; 146 return false; 147 }; 148 149 observe.isString = function(str) { // 判断str是否为字符串 150 return typeof str === 'string'; 151 }; 152 153 observe.isFunction = function(fn) { // 判断参数fn是否为函数 154 return Object.prototype.toString.call(fn) === '[object Function]'; 155 }; 156 157 observe._getRootName = function (prop, path) { // 返回属性名或者# 158 if (path === '#') { 159 return prop; 160 }; 161 return path.split('-')[1]; 162 }; 163 164 observe.add = function(obj, prop) { // 给对象添加属性且监听这个添加的属性 165 var $observer = obj.$observer; 166 $observer.watch(obj, prop); 167 }; 168 169 observe.set = function(obj, prop, value, exec) { // 给对象添加属性且赋值, exec表示赋值后是否要回调一次监听函数 170 if (!exec) { 171 obj[prop] = value; 172 }; 173 var $observer = obj.$observer; 174 $observer.watch(obj, prop); 175 if (exec) { 176 obj[prop] = value; 177 }; 178 }; 179 180 Array.prototype.size = function(length) { // 数组size()方法 181 this.length = length; 182 }; 183 184 185 // 抛出去 186 if (typeof module != 'undefined' && module.exports && this.module !== module) { 187 module.exports = observe; 188 } else if (typeof define === 'function' && define.amd) { 189 define(observe); 190 } else { 191 win.observe = observe; 192 }; 193 194 })(Function('return this')());
使用姿势:
// 对象字面量 var obj = { a: 1, b: 2, arr: [1, 2, [3, 4, 5]], callback: function(name, value, old) { console.log(name + '__' + value + '__' + old); } }; observe(obj, function(name, value, old, path) { console.log(name + '__' + value + '__' + old); console.log(path); }); console.log(obj); var proxy = new Proxy(obj, { get(target, prop, receiver) { return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { var old = target[prop]; if (old !== value) { obj.callback.call(target, prop, value, old); return Reflect.set(target, prop, value, receiver); } } }); // 数组 var arr = ['a', 'b', 'c']; observe(arr, function(name, value, old) { console.log(name + '__' + value + '__' + old); }); arr.push('d'); arr[3] = 'dd'; // 复杂对象 var complexObj = { a: 1, b: 2, c: [{d: [4]}] }; observe(complexObj, function(name, value, old, path) { console.log(name + '__' + value + '__' + old); console.log(path); }); complexObj.c[0].d = 100; observe.set(complexObj, 'd', 'ddd', true); observe.add(complexObj, 'e'); // 普通对象 var User = function(name, age) { this.name = name; this.age = age; // 只监听name observe(this, ['name'], function(name, value, oldValue) { console.log(name + '__' + value + '__' + oldValue); }); }; var user = new User('lisi', 25); user.name = 'wangwu'; user.age = 20;
ps:
dnt大神写的一个观察任意对象任意属性变化的一个轻量级库。
个人很喜欢,很不错。
es6 的 Proxy 和 Reflect 很强大,也许哪天项目中使用了Proxy 和 Reflect,那此库可能就不用了。
源码地址: https://github.com/dntzhang/oba