obaa源码加注
这个是dntzhang写的用于监听变量更改的库obaa,加上一点注释方便理解~
/* obaa 1.0.0
* By dntzhang
* Github: https://github.com/Tencent/omi
* MIT Licensed.
*/
; (function (win) {
var obaa = function (target, arr, callback) {
var _observe = function (target, arr, callback) {
//if (!target.$observer) target.$observer = this
// $observer指向这个要返回的对象
var $observer = this
// 被监听的属性列表
var eventPropArr = []
// 如果是数组先监听数组的变异操作
if (obaa.isArray(target)) {
if (target.length === 0) {
target.$observeProps = {}
target.$observeProps.$observerPath = '#'
}
$observer.mock(target)
}
// 遍历非继承的key值
for (var prop in target) {
if (target.hasOwnProperty(prop)) {
// 传入三个参数时(指定了要监听的key值)
if (callback) {
// 以array方式指定
if (obaa.isArray(arr) && obaa.isInArray(arr, prop)) {
eventPropArr.push(prop)
$observer.watch(target, prop)
}
// 以string方式指定
else if (obaa.isString(arr) && prop == arr) {
eventPropArr.push(prop)
$observer.watch(target, prop)
}
}
// 传入两个参数时(代表监听所有的key值)
else {
eventPropArr.push(prop)
$observer.watch(target, prop)
}
}
}
// 留一个原始对象的引用(后面返回的时候用到)
$observer.target = target
// 如果没有 handler 队列,初始化一个
if (!$observer.propertyChangedHandler)
$observer.propertyChangedHandler = []
// 根据参数个数赋值回调函数
var propChanged = callback ? callback : arr
// 把回调放入 handler 队列中
// 下面的all代表了监听所有的key
$observer.propertyChangedHandler.push({
all: !callback,
propChanged: propChanged,
eventPropArr: eventPropArr
})
}
_observe.prototype = {
// 当有值修改时的的响应函数
// 这个方法定义在原型上了,所有对象都共用这个方法
onPropertyChanged: function (prop, value, oldValue, target, path) {
// 如果值有变化且当前对象有 Handler
if (value !== oldValue && this.propertyChangedHandler) {
// 根的名字?
var rootName = obaa._getRootName(prop, path)
// 遍历当前对象所有的 Handler
for (
var i = 0, len = this.propertyChangedHandler.length;
i < len;
i++
) {
var handler = this.propertyChangedHandler[i]
// .all - 当前监听所有变化
// isInArray - 当前节点在 handler 监听列表中
// rootName 以 Array- 开头
if (
handler.all ||
obaa.isInArray(handler.eventPropArr, rootName) ||
rootName.indexOf('Array-') === 0
) {
// 调用 handler 的 propChanged 方法
// 并把 上下文设为 this.target
handler.propChanged.call(this.target, prop, value, oldValue, path)
}
}
}
// 如果属性不以 Array- 开头(不是数组的操作方法) && 值为 object(数组、对象或者null)
if (prop.indexOf('Array-') !== 0 && typeof value === 'object') {
// 监听这个属性
// 应该是为了保证新加入的属性也能被监听到?
this.watch(target, prop, target.$observeProps.$observerPath)
}
},
// 模拟数组操作(覆盖原有的数组操作方法)
// 作用是监听数组的操作方法
mock: function (target) {
var self = this
// 修改操作数组的方法,这里的item是push、splice这些方法名字符串
obaa.methods.forEach(function (item) {
target[item] = function () {
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(obaa.triggerStr)) {
// 遍历target的所有非继承得到的、非可执行函数的属性
// 对这个属性调用watch方法
for (var cprop in this) {
if (
this.hasOwnProperty(cprop) &&
!obaa.isFunction(this[cprop])
) {
self.watch(this, cprop, this.$observeProps.$observerPath)
}
}
//todo
// 以 Array- 开头加上 方法名,调用 onPropertyChanged 方法
self.onPropertyChanged(
'Array-' + item,
this,
old,
this,
this.$observeProps.$observerPath
)
}
return result
}
// 把原来的数组操作方法用 purePush 这种命名暴露出去
// 大概是为了提供一个能够直接操作但不被监听到的方法
target[
'pure' + 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) {
// 不监听用来监听的属性(大概是为了避免循环中修改了存储用的属性)
if (prop === '$observeProps' || prop === '$observer') return
// 不监听函数
if (obaa.isFunction(target[prop])) return
// 没有的话初始化监听属性
if (!target.$observeProps) target.$observeProps = {}
// 如果传入path,把path存入observeProps
// 如果未传path(代表是根节点),把path设为 ‘#’
if (path !== undefined) {
target.$observeProps.$observerPath = path
} else {
target.$observeProps.$observerPath = '#'
}
var self = this
// 把target的属性值复制到 $observeProps 里,
// 用 currentValue 表示当前复制的值
var currentValue = (target.$observeProps[prop] = target[prop])
// 使用getter和setter对属性进行监听
// 操作时使用$observeProps进行操作
// 当被赋值时调用对象的 onPropertyChanged 方法(让对象自己处理响应)
Object.defineProperty(target, prop, {
get: function () {
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
)
}
})
// 如果当前遍历到的值是对象(数组)的话
if (typeof currentValue == 'object') {
if (obaa.isArray(currentValue)) {
// 递归模拟数组的操作方法
this.mock(currentValue)
// 如果是空数组
// 初始化对象的 $observeProps
// 初始化对象的 $observerPath
if (currentValue.length === 0) {
if (!currentValue.$observeProps) currentValue.$observeProps = {}
if (path !== undefined) {
currentValue.$observeProps.$observerPath = path
} else {
currentValue.$observeProps.$observerPath = '#'
}
}
}
// 遍历那些非继承得到的属性
for (var cprop in currentValue) {
if (currentValue.hasOwnProperty(cprop)) {
// 对这些属性进行监听
this.watch(
currentValue,
cprop,
target.$observeProps.$observerPath + '-' + prop
)
}
}
}
}
}
// 在这个omi-mp-create里未用到这个返回值
return new _observe(target, arr, callback)
}
// 数组被重新修改了的方法列表
// (或者说被监听了)
obaa.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'
]
// 数组方法中会触发事件的方法列表(最后被join了一下成一个字符串)
obaa.triggerStr = [
'concat',
'copyWithin',
'fill',
'pop',
'push',
'reverse',
'shift',
'sort',
'splice',
'unshift',
'size'
].join(',')
obaa.isArray = function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
obaa.isString = function (obj) {
return typeof obj === 'string'
}
obaa.isInArray = function (arr, item) {
for (var i = arr.length; --i > -1;) {
if (item === arr[i]) return true
}
return false
}
obaa.isFunction = function (obj) {
return Object.prototype.toString.call(obj) == '[object Function]'
}
// 得到根的名字
// 如果已经是根则返回当前属性名字(现在这个就是根)
// 否则返回path中最上级的那一层
obaa._getRootName = function (prop, path) {
if (path === '#') {
return prop
}
return path.split('-')[1]
}
// 暴露的方法,作用是监听obj的某个已存在属性
obaa.add = function (obj, prop) {
var $observer = obj.$observer
$observer.watch(obj, prop)
}
// 暴露的方法,用于为obj赋值一个属性并监听这个属性
obaa.set = function (obj, prop, value, exec) {
if (!exec) {
obj[prop] = value
}
var $observer = obj.$observer
$observer.watch(obj, prop)
if (exec) {
obj[prop] = value
}
}
// 额外的size方法,使用size修改数组长度而非直接赋值
// 避免js不能监听数组.length属性的问题
Array.prototype.size = function (length) {
this.length = length
}
// 判断运行环境,把obaa对象暴露出去
if (
typeof module != 'undefined' &&
module.exports
) {
module.exports = obaa
} else if (typeof define === 'function' && define.amd) {
define(obaa)
} else {
win.obaa = obaa
}
})(Function('return this')())
// 这里使用Function是为了取到全局变量的this,也就是window对象
// 见MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function
obaa源码加注的更多相关文章
- omi-mp-create源码加注
omi-mp-create是dntzhang写的小程序框架,主要功能是实现全局状态自动更新和页面间通信,传送门. 代码虽然简单但是注释不多读起来还是需要一点时间理解,因此在上面加入了个人理解的注释方便 ...
- 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01
百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...
- 鸿蒙内核源码分析(静态站点篇) | 五一哪也没去就干了这事 | 百篇博客分析OpenHarmony源码 | v52.02
百篇博客系列篇.本篇为: v52.xx 鸿蒙内核源码分析(静态站点篇) | 五一哪也没去就干了这事 | 51.c.h.o 前因后果相关篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...
- 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙看这篇或许真的够了 | 百篇博客分析OpenHarmony源码 | v50.06
百篇博客系列篇.本篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉坑指南 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉 ...
- 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02
百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...
- 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02
百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...
- 鸿蒙源码分析系列(总目录) | 百万汉字注解 百篇博客分析 | 深入挖透OpenHarmony源码 | v8.23
百篇博客系列篇.本篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o 百篇博客.往期回顾 在给OpenHarmony内核源码加注过程中,整理出以下 ...
- Flink架构,源码及debug
序 工作中用Flink做批量和流式处理有段时间了,感觉只看Flink文档是对Flink ProgramRuntime的细节描述不是很多, 程序员还是看代码最简单和有效.所以想写点东西,记录一下,如果能 ...
- 推荐算法_CIKM-2019-AnalytiCup 冠军源码解读
最近在帮一初创app写推荐系统,顺便学习一波用户兴趣高速检索的冠军算法. 写总结前贴出冠军代码的git地址:https://github.com/ChuanyuXue/CIKM-2019-Analyt ...
随机推荐
- iOS系统结构
应用交互层.多媒体层.核心服务层.系统层. 参考官方文档apple Develop GuidesiOS Technologies IOS分为四级结构,由上到下为可触摸层,媒体层,核心服务层,核心系统层 ...
- nginx上搭建https
nginx上配置https的条件: 1.SSL证书和服务器私钥文件 2.nginx支持SSL模块 一.获取SSL证书 网上有提供权威认证的SSL证书的网站,但多数是收费的,而且不便宜.在正式的生产环境 ...
- 加减法计算器-java
由于经常进行较大数据的加减法计算,好多计算器都是转换成科学技术法的,所以自己用java写了一个 功能如下: 1,可以输入多个带千位分隔符的数字,进行加减法计算 2,结果展示带千位分隔符 3,结果展示不 ...
- ASP.NET Menu控件点击区域太小解决方法
ASP.NET自带的Menu控件点击区域比较小,基本就是文本范围和图片范围,在区域外虽然选择的项有颜色变化,但是这个时候点击是没有用的,体验不是很好 检查前台生成的HTML,是用td嵌套a标签,a标签 ...
- [luogu2059 JLOI2013] 卡牌游戏 (概率dp)
题目描述 N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随机(即按相等的概率)从卡牌堆里选择一张卡片,假设卡片上的数字为X,则庄家首先把 ...
- STM32 抢占优先级和响应优先级
一.抢占优先级和响应优先级 STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号 越小,表明它的优先级别越高. 抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中 ...
- Js原生实现抽奖功能
<div>代码 按钮代码 JS原生代码 完整的代码: <div style="width:365px;height:300px;border:2px solid gree ...
- 2015 Multi-University Training Contest 6 hdu 5358 First One
First One Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total ...
- QT5 OpenGL (六, 键盘事件, 开关灯,放大缩小综合运用)
概要 实例效果图 立体图放大图 立体图缩小图 不加矢量开灯图 不加矢量关灯图 加矢量关灯图1 加矢量关灯图2 部分代码展示 主要内容解析 QT键盘事件 立体图形的放大和缩小 上下左右键以及A键D争键控 ...
- iOS 开发仿网易云音乐歌词海报
使用网易云音乐也是一个巧合,我之前一直使用QQ音乐听歌,前几天下 app 手机内存告急.于是就把QQ音乐给卸载掉了,正好晚上朋友圈里有一个朋友用网易云音乐分享了一首歌曲,于是我也就尝试下载了网易云音乐 ...