/**
* observejs --- By dnt http://kmdjs.github.io/
* Github: https://github.com/kmdjs/observejs
* MIT Licensed.
* Sorrow.X --- 添加注释,注释纯属个人理解
*/
; (function(win) { var observe = function(target, arr, callback) { var _observe = function(target, arr, callback) { // target: 监听对象, arr: 监听对象的属性列表, callback: 回调函数
if (!target.$observer) target.$observer = this; // 给target监听对象添加$observer属性,值为_observe构造函数的实例
var $observer = target.$observer; // 把实例赋值给$observer变量
var eventPropArr = []; //事件属性列表
if (observe.isArray(target)) { // 监听对象如果是数组
if (target.length === 0) { // 如果是个空数组
target.$observeProps = {}; // 给监听对象添加新属性$observeProps且赋值为{}空对象
target.$observeProps.$observerPath = '#'; // 给监听对象的属性$observeProps对象添加$observerPath属性且赋值为'#'
};
$observer.mock(target); // 调用原型上的mock方法(给target加上数组的方法)
};
for (var prop in target) { // 遍历监听对象属性(含原型上的属性)
if (target.hasOwnProperty(prop)) { // 只对对象自身的属性感兴趣(不要原型上的)
if (callback) { // 如果用户传了三个参数的话
if (observe.isArray(arr) && observe.isInArray(arr, prop)) { // arr如果是数组且prop属性在数组中
eventPropArr.push(prop);
$observer.watch(target, prop);
} else if (observe.isString(arr) && prop == arr) { // arr如果是字符串且prop属性与arr字符串一样
eventPropArr.push(prop);
$observer.watch(target, prop);
};
} else {
eventPropArr.push(prop); //添加target的属性到eventPropArr数组
$observer.watch(target, prop); // 调用原型上的watch方法(给属性添加监听)
};
};
};
$observer.target = target; // 给$observer对象添加target属性
if (!$observer.propertyChangedHandler) $observer.propertyChangedHandler = []; // 给$observer对象添加属性propertyChangedHandler,值为空数组
var propChanged = callback ? callback : arr; // propChanged存储回调函数
$observer.propertyChangedHandler.push({ // 给$observer对象或者target.$observer对象(其实就是this实例啦)属性propertyChangedHandler数组添加一个对象
all: !callback,
propChanged: propChanged,
eventPropArr: eventPropArr
});
}; _observe.prototype = { // 原型 "onPropertyChanged": function(prop, value, oldValue, target, path) { // prop: 属性, value: 设置的新值, oldValue: 上一次属性的值, target: 监听对象, path: 路径
if (value !== oldValue && this.propertyChangedHandler) { // prop的新旧值不同且实例的propertyChangedHandler属性为真值
var rootName = observe._getRootName(prop, path);
for (var i = 0, len = this.propertyChangedHandler.length; i < len; i++) { // 循环遍历
var handler = this.propertyChangedHandler[i]; // 数组成员
if (handler.all || observe.isInArray(handler.eventPropArr, rootName) || rootName.indexOf("Array-") === 0) {
handler.propChanged.call(this.target, prop, value, oldValue, path); // 执行用户的回调函数
};
};
};
if (prop.indexOf('Array-') !== 0 && typeof value === 'object') { // prop字符串不包含'Array-'且value是array或者object
this.watch(target, prop, target.$observeProps.$observerPath); // 属性为对象或者数组,再次对其属性进行监听
};
}, "mock": function(target) { // target: 数组
var self = this; // 存个实例
observe.methods.forEach(function(item) { // 遍历数组的每个方法
target[item] = function() { // 给target数组对象添加方法
var old = Array.prototype.slice.call(this, 0); // 把原始数组存一下
var result = Array.prototype[item].apply(this, Array.prototype.slice.call(arguments)); // 数组不同方法的返回值
if (new RegExp("\\b" + item + "\\b").test(observe.triggerStr)) {
for (var cprop in this) {
if (this.hasOwnProperty(cprop) && !observe.isFunction(this[cprop])) { // 数组含有可枚举的属性并且属性不是函数
self.watch(this, cprop, this.$observeProps.$observerPath); // 对数组的属性进行监听(以前的属性也重新再次监听了,我觉得不太好,可以改进,不知道理解错了没)
};
};
// todo
self.onPropertyChanged("Array-" + item, this, old, this, this.$observeProps.$observerPath);
};
return result;
};
target['real' + item.substring(0, 1).toUpperCase() + item.substring(1)] = function() { // 返回数组方法真实的的结果(其实就是调用数组的方法)
return Array.prototype[item].apply(this, Array.prototype.slice.call(arguments));
};
});
}, "watch": function(target, prop, path) { // target: 监听对象, prop: 监听对象的属性, path: 调用路径
if (prop === "$observeProps" || prop === "$observer") return; // 如果监听对象的属性等于$observeProps, $observer这2个属性中的任何一个,就return掉了
if (observe.isFunction(target[prop])) return; // 如果监听对象的属性类型是函数,也return掉
if (!target.$observeProps) target.$observeProps = {}; // 如果target对象没有$observeProps属性,则加上这个属性且值为{}, 有的话就跳过
if (path !== undefined) { // 如果路径不为空,则设置$observerPath值为path
target.$observeProps.$observerPath = path;
} else { // 否则默认'#'
target.$observeProps.$observerPath = '#';
};
var self = this; // self存个_observe实例对象
var currentValue = target.$observeProps[prop] = target[prop]; // 当前属性的value值(target.$observeProps对象添加了这个prop属性且有值)
Object.defineProperty(target, prop, { // 给target对象的属性添加set, get方法
get: function() { // 返回 target.$observeProps属性的值
return this.$observeProps[prop];
},
set: function(value) {
var old = this.$observeProps[prop]; // 存一下上一次的属性值
this.$observeProps[prop] = value; // 设置新的属性值
self.onPropertyChanged(prop, value, old, this, target.$observeProps.$observerPath); // 设置值时,触发实例onPropertyChanged方法
}
});
if (typeof currentValue == 'object') { // 如果属性值是array或者object
if (observe.isArray(currentValue)) { // 如果是数组
this.mock(currentValue); // 调用原型上的mock方法
if (currentValue.length === 0) { // 如果是空数组
if (!currentValue.$observeProps) currentValue.$observeProps = {}; // 如果currentValue对象没有$observeProps属性,则加上这个属性且值为{}, 有的话就跳过
if (path !== undefined) { // 如果路径不为空,则设置currentValue.$observerPath值为path
currentValue.$observeProps.$observerPath = path;
} else { // 否则默认'#'
currentValue.$observeProps.$observerPath = '#';
};
};
};
for (var cprop in currentValue) { // 循环currentValue对象的每一个成员
if (currentValue.hasOwnProperty(cprop)) { // 只对对象自身的属性感兴趣(不要原型上的)
this.watch(currentValue, cprop, target.$observeProps.$observerPath + '-' + prop); // 再次监听(递归)
};
};
};
}
}; return new _observe(target, arr, callback); // 实例化_observe构造函数
}; // observe的静态属性
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"];
observe.triggerStr = ["concat", "copyWithin", "fill", "pop", "push", "reverse", "shift", "sort", "splice", "unshift", "size"].join(","); // observe的静态方法(工具函数)
observe.isArray = function(arr) { // 判断参数arr是否为数组
return Object.prototype.toString.call(arr) === '[object Array]';
}; observe.isInArray = function(arr, item) { // 判断item是否在数组arr中
for (var i = arr.length; --i > -1; ) {
if (item === arr[i]) return true;
};
return false;
}; observe.isString = function(str) { // 判断str是否为字符串
return typeof str === 'string';
}; observe.isFunction = function(fn) { // 判断参数fn是否为函数
return Object.prototype.toString.call(fn) === '[object Function]';
}; observe._getRootName = function (prop, path) { // 返回属性名或者#
if (path === '#') {
return prop;
};
return path.split('-')[1];
}; observe.add = function(obj, prop) { // 给对象添加属性且监听这个添加的属性
var $observer = obj.$observer;
$observer.watch(obj, prop);
}; observe.set = function(obj, prop, value, exec) { // 给对象添加属性且赋值, exec表示赋值后是否要回调一次监听函数
if (!exec) {
obj[prop] = value;
};
var $observer = obj.$observer;
$observer.watch(obj, prop);
if (exec) {
obj[prop] = value;
};
}; Array.prototype.size = function(length) { // 数组size()方法
this.length = length;
}; // 抛出去
if (typeof module != 'undefined' && module.exports && this.module !== module) {
module.exports = observe;
} else if (typeof define === 'function' && define.amd) {
define(observe);
} else {
win.observe = observe;
}; })(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

observe.js 源码 学习笔记的更多相关文章

  1. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  2. Underscore.js 源码学习笔记(上)

    版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}());  这样的东西,我们应该知道这是一个 IIFE(立即执行 ...

  3. Vue.js 源码学习笔记

    最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些.那么,就让我来吧:) 程序 ...

  4. AlloyTouch.js 源码 学习笔记及原理说明

    alloyTouch这个库其实可以做很多事的, 比较抽象, 需要我们用户好好的思考作者提供的实例属性和一些回调方法(touchStart, change, touchMove, pressMove, ...

  5. move.js 源码 学习笔记

    源码笔记: /* move.js * @author:flfwzgl https://github.com/flfwzgl * @copyright: MIT license * Sorrow.X - ...

  6. lazy-load-img.js 源码 学习笔记及原理说明

    lazy-load-img.js? 1. 什么鬼? 一个轻量级的图片懒加载,我个人很是喜欢. 2. 有什么优势? 1.原生js开发,不依赖任何框架或库 2.支持将各种宽高不一致的图片,自动剪切成默认图 ...

  7. AlloyFinger.js 源码 学习笔记及原理说明

    此手势库利用了手机端touchstart, touchmove, touchend, touchcancel原生事件模拟出了 rotate  touchStart  multipointStart   ...

  8. Vue.js 源码学习笔记 -- 分析前准备2 -- Object.defineProperty

    解析神奇的 Object.defineProperty   几行代码看他怎么用   var a= {} Object.defineProperty( a, "b", { value ...

  9. Vue.js 源码学习笔记 -- 分析前准备1 -- vue三大利器

    主体 实例方法归类:   先看个作者推荐, 清晰易懂的  23232 简易编译器   重点: 最简单的订阅者模式 // Observer class Observer { constructor (d ...

随机推荐

  1. 解决Apache的错误日志巨大的问题以及关闭Apache web日志记录

    调整错误日志的级别 这几天 apache错误日志巨大 莫名其妙的30G  而且 很多都是那种页面不存在的  网站太多了  死链接相应的也很多于是把错误警告调低了 因为写日志会给系统带来很大的损耗.关闭 ...

  2. DPM,DEM,DDPM的区别

    此文来自我在CFD中国论坛中的一篇回复:http://www.cfd-china.com/topic/58/dem%E5%92%8Cdpm/21 正好这几天在研究fluent里的DEM,DPM和DDP ...

  3. 关于老版本ubuntu源不能用的问题

    在解决方向键为大写ABCD时安装vim 我的是Ubuntu 10.10 老版本 输入 sudo apt-get install vim 时出现 Package 'vim' has no install ...

  4. Tomcat 使用过程中的一些技巧

    url中文地址乱码 原因: tomcat默认的在url传输时是用iso8859-1编码. 解决方案一: 在使用get传输参数时,将参数中的中文转换成url格式,也就是使用urlEncode和urlDe ...

  5. 让EFCore更疯狂些的扩展类库(二):查询缓存、分部sql、表名替换的策略配置

    前言 上一篇介绍了扩展类库的功能简介,通过json文件配置sql语句 和 sql语句的直接执行,这篇开始说明sql配置的策略模块:策略管理器与各种策略的配置. 类库源码:github:https:// ...

  6. BZOJ-2768: [JLOI2010]冠军调查(超级裸的最小割)

    2768: [JLOI2010]冠军调查 Time Limit: 10 Sec  Memory Limit: 128 MB Description 一年一度的欧洲足球冠军联赛已经进入了淘汰赛阶段.随着 ...

  7. oracle__wm_concat函数

    首先让我们来看看这个神奇的函数wm_concat(列名),该函数可以把列值以","号分隔起来,并显示成一行,接下来上例子,看看这个神奇的函数如何应用 准备测试数据 SQL> ...

  8. Python开发项目:大型模拟战争游戏(外星人入侵)

    外星人入侵 游戏概述: 现在准备用python开始搞一个大型游戏,模拟未来战争,地球人狙击外星人大战(其实就是小蜜蜂游戏2333),玩家控制一个飞船,用子弹歼灭屏幕上空的外星飞船:项目用到了Pygam ...

  9. matlab switch case 和 try catch用法示例

    %清除变量或指令 clc;clear; % 允许用户输入参数 disp ('该功能练习switch语句'); disp ('输入1-10其中一个数,系统判定奇偶. '); count = input ...

  10. 基于Spring DM管理的Bundle获取Spring上下文对象及指定Bean对象

    在讲述服务注册与引用的随笔中,有提到context.getServiceReferences()方法,通过该方法可以获取到OSGI框架容器中的指定类型的服务引用,从而获取到对应的服务对象.同时该方法还 ...