zoukankan      html  css  js  c++  java
  • 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

    开心的做一个无忧无虑的码农,争取每天进步一点。
  • 相关阅读:
    重写不受限制
    类的向上转型(安全)向下转型(不安全)
    类的继承之构造函数和析构函数的顺序
    父类和子类的截断现象
    派生类重写方法
    运算符重载总结(大全)
    运算符重载方法3
    运算符重载方法2
    Shell基础编程
    TCP Wrappers(简单防火墙)---限制IP登录ssh
  • 原文地址:https://www.cnblogs.com/sorrowx/p/6489025.html
Copyright © 2011-2022 走看看