observe.js 源码 学习笔记

  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

posted @ 2017-03-02 09:50  Sorrow.X  阅读(501)  评论(0编辑  收藏  举报