obaa v2.0
* Better unit testing * Fix bugs that new attributes cannot watch * More functional's source code * More concise naming
This commit is contained in:
parent
7e2df1e27e
commit
fcfe12d9f7
|
@ -64,6 +64,10 @@ obj.c = 4 //trigger observe callback
|
|||
//if obj.c is undefined
|
||||
obj.c = 3
|
||||
obj.c = 4 //don't trigger observe callback
|
||||
|
||||
//add new observer prop
|
||||
obaa.add(obj, 'd')
|
||||
obj.d = 1 //trigger observe callback
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
|
@ -1,164 +1,70 @@
|
|||
/* obaa 1.0.0
|
||||
/*
|
||||
* obaa 2.0.0
|
||||
* By dntzhang
|
||||
* Github: https://github.com/Tencent/omi
|
||||
* Github: https://github.com/Tencent/omi/tree/master/packages/obaa
|
||||
* MIT Licensed.
|
||||
*/
|
||||
;(function(win) {
|
||||
var obaa = function(target, arr, callback) {
|
||||
var _observe = function(target, arr, callback) {
|
||||
if (!target.$observer) target.$observer = this
|
||||
var $observer = target.$observer
|
||||
var eventPropArr = []
|
||||
if (obaa.isArray(target)) {
|
||||
if (target.length === 0) {
|
||||
target.$observeProps = {}
|
||||
target.$observeProps.$observerPath = '#'
|
||||
|
||||
|
||||
// __r_: root
|
||||
// __c_: prop change callback
|
||||
// __p_: path
|
||||
|
||||
; (function (win) {
|
||||
function obaa(target, arr, callback) {
|
||||
|
||||
var eventPropArr = []
|
||||
if (isArray(target)) {
|
||||
if (target.length === 0) {
|
||||
target.__o_ = {
|
||||
__r_: target,
|
||||
__p_: '#'
|
||||
}
|
||||
$observer.mock(target)
|
||||
}
|
||||
for (var prop in target) {
|
||||
if (target.hasOwnProperty(prop)) {
|
||||
if (callback) {
|
||||
if (obaa.isArray(arr) && obaa.isInArray(arr, prop)) {
|
||||
eventPropArr.push(prop)
|
||||
$observer.watch(target, prop)
|
||||
} else if (obaa.isString(arr) && prop == arr) {
|
||||
eventPropArr.push(prop)
|
||||
$observer.watch(target, prop)
|
||||
}
|
||||
} else {
|
||||
mock(target, target)
|
||||
}
|
||||
for (var prop in target) {
|
||||
if (target.hasOwnProperty(prop)) {
|
||||
if (callback) {
|
||||
if (isArray(arr) && isInArray(arr, prop)) {
|
||||
eventPropArr.push(prop)
|
||||
$observer.watch(target, prop)
|
||||
watch(target, prop, null, target)
|
||||
} else if (isString(arr) && prop == arr) {
|
||||
eventPropArr.push(prop)
|
||||
watch(target, prop, null, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
$observer.target = target
|
||||
if (!$observer.propertyChangedHandler)
|
||||
$observer.propertyChangedHandler = []
|
||||
var propChanged = callback ? callback : arr
|
||||
$observer.propertyChangedHandler.push({
|
||||
all: !callback,
|
||||
propChanged: propChanged,
|
||||
eventPropArr: eventPropArr
|
||||
})
|
||||
}
|
||||
_observe.prototype = {
|
||||
onPropertyChanged: function(prop, value, oldValue, target, path) {
|
||||
if (value !== oldValue && this.propertyChangedHandler) {
|
||||
var rootName = obaa._getRootName(prop, path)
|
||||
for (
|
||||
var i = 0, len = this.propertyChangedHandler.length;
|
||||
i < len;
|
||||
i++
|
||||
) {
|
||||
var handler = this.propertyChangedHandler[i]
|
||||
if (
|
||||
handler.all ||
|
||||
obaa.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') {
|
||||
this.watch(target, prop, target.$observeProps.$observerPath)
|
||||
}
|
||||
},
|
||||
mock: function(target) {
|
||||
var self = this
|
||||
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)) {
|
||||
for (var cprop in this) {
|
||||
if (
|
||||
this.hasOwnProperty(cprop) &&
|
||||
!obaa.isFunction(this[cprop])
|
||||
) {
|
||||
self.watch(this, cprop, this.$observeProps.$observerPath)
|
||||
}
|
||||
}
|
||||
//todo
|
||||
self.onPropertyChanged(
|
||||
'Array-' + item,
|
||||
this,
|
||||
old,
|
||||
this,
|
||||
this.$observeProps.$observerPath
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
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 = {}
|
||||
if (path !== undefined) {
|
||||
target.$observeProps.$observerPath = path
|
||||
} else {
|
||||
target.$observeProps.$observerPath = '#'
|
||||
}
|
||||
var self = this
|
||||
var currentValue = (target.$observeProps[prop] = target[prop])
|
||||
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)
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
eventPropArr.push(prop)
|
||||
watch(target, prop, null, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
return new _observe(target, arr, callback)
|
||||
if (!target.__c_) {
|
||||
target.__c_ = []
|
||||
}
|
||||
var propChanged = callback ? callback : arr
|
||||
target.__c_.push({
|
||||
all: !callback,
|
||||
propChanged: propChanged,
|
||||
eventPropArr: eventPropArr
|
||||
})
|
||||
}
|
||||
|
||||
obaa.methods = [
|
||||
var triggerStr = [
|
||||
'concat',
|
||||
'copyWithin',
|
||||
'fill',
|
||||
'pop',
|
||||
'push',
|
||||
'reverse',
|
||||
'shift',
|
||||
'sort',
|
||||
'splice',
|
||||
'unshift',
|
||||
'size'
|
||||
].join(',')
|
||||
|
||||
var methods = [
|
||||
'concat',
|
||||
'copyWithin',
|
||||
'entries',
|
||||
|
@ -190,63 +96,168 @@
|
|||
'values',
|
||||
'size'
|
||||
]
|
||||
obaa.triggerStr = [
|
||||
'concat',
|
||||
'copyWithin',
|
||||
'fill',
|
||||
'pop',
|
||||
'push',
|
||||
'reverse',
|
||||
'shift',
|
||||
'sort',
|
||||
'splice',
|
||||
'unshift',
|
||||
'size'
|
||||
].join(',')
|
||||
|
||||
obaa.isArray = function(obj) {
|
||||
function mock(target, root) {
|
||||
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(triggerStr)) {
|
||||
for (var cprop in this) {
|
||||
if (
|
||||
this.hasOwnProperty(cprop) &&
|
||||
!isFunction(this[cprop])
|
||||
) {
|
||||
watch(this, cprop, this.__o_.__p_, root)
|
||||
}
|
||||
}
|
||||
//todo
|
||||
onPropertyChanged(
|
||||
'Array-' + item,
|
||||
this,
|
||||
old,
|
||||
this,
|
||||
this.__o_.__p_,
|
||||
root
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
target[
|
||||
'pure' + item.substring(0, 1).toUpperCase() + item.substring(1)
|
||||
] = function () {
|
||||
return Array.prototype[item].apply(
|
||||
this,
|
||||
Array.prototype.slice.call(arguments)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function watch(target, prop, path, root) {
|
||||
if (prop === '__o_') return
|
||||
if (isFunction(target[prop])) return
|
||||
if (!target.__o_) target.__o_ = {
|
||||
__r_: root
|
||||
}
|
||||
if (path !== undefined && path !== null) {
|
||||
target.__o_.__p_ = path
|
||||
} else {
|
||||
target.__o_.__p_ = '#'
|
||||
}
|
||||
|
||||
var currentValue = (target.__o_[prop] = target[prop])
|
||||
Object.defineProperty(target, prop, {
|
||||
get: function () {
|
||||
return this.__o_[prop]
|
||||
},
|
||||
set: function (value) {
|
||||
var old = this.__o_[prop]
|
||||
this.__o_[prop] = value
|
||||
onPropertyChanged(
|
||||
prop,
|
||||
value,
|
||||
old,
|
||||
this,
|
||||
target.__o_.__p_,
|
||||
root
|
||||
)
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
})
|
||||
if (typeof currentValue == 'object') {
|
||||
if (isArray(currentValue)) {
|
||||
mock(currentValue, root)
|
||||
if (currentValue.length === 0) {
|
||||
if (!currentValue.__o_) currentValue.__o_ = {}
|
||||
if (path !== undefined) {
|
||||
currentValue.__o_.__p_ = path
|
||||
} else {
|
||||
currentValue.__o_.__p_ = '#'
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var cprop in currentValue) {
|
||||
if (currentValue.hasOwnProperty(cprop)) {
|
||||
watch(
|
||||
currentValue,
|
||||
cprop,
|
||||
target.__o_.__p_ + '-' + prop,
|
||||
root
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onPropertyChanged(prop, value, oldValue, target, path, root) {
|
||||
if (value !== oldValue && root.__c_) {
|
||||
var rootName = getRootName(prop, path)
|
||||
for (
|
||||
var i = 0, len = root.__c_.length;
|
||||
i < len;
|
||||
i++
|
||||
) {
|
||||
var handler = root.__c_[i]
|
||||
if (
|
||||
handler.all ||
|
||||
isInArray(handler.eventPropArr, rootName) ||
|
||||
rootName.indexOf('Array-') === 0
|
||||
) {
|
||||
handler.propChanged.call(target, prop, value, oldValue, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prop.indexOf('Array-') !== 0 && typeof value === 'object') {
|
||||
watch(target, prop, target.__o_.__p_, root)
|
||||
}
|
||||
}
|
||||
|
||||
function isFunction(obj) {
|
||||
return Object.prototype.toString.call(obj) == '[object Function]'
|
||||
}
|
||||
|
||||
|
||||
|
||||
function isArray(obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Array]'
|
||||
}
|
||||
|
||||
obaa.isString = function(obj) {
|
||||
function isString(obj) {
|
||||
return typeof obj === 'string'
|
||||
}
|
||||
|
||||
obaa.isInArray = function(arr, item) {
|
||||
for (var i = arr.length; --i > -1; ) {
|
||||
function isInArray(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]'
|
||||
}
|
||||
|
||||
obaa._getRootName = function(prop, path) {
|
||||
function getRootName(prop, path) {
|
||||
if (path === '#') {
|
||||
return prop
|
||||
}
|
||||
return path.split('-')[1]
|
||||
}
|
||||
|
||||
obaa.add = function(obj, prop) {
|
||||
var $observer = obj.$observer
|
||||
$observer.watch(obj, prop)
|
||||
obaa.add = function (obj, prop) {
|
||||
watch(obj, prop, obj.__o_.__p_, obj.__o_.__r_)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
obaa.set = function (obj, prop, value) {
|
||||
watch(obj, prop, obj.__o_.__p_, obj.__o_.__r_)
|
||||
obj[prop] = value
|
||||
}
|
||||
|
||||
Array.prototype.size = function(length) {
|
||||
Array.prototype.size = function (length) {
|
||||
this.length = length
|
||||
}
|
||||
|
||||
|
@ -259,4 +270,4 @@
|
|||
} else {
|
||||
win.obaa = obaa
|
||||
}
|
||||
})(Function('return this')())
|
||||
})(Function('return this')());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "obaa",
|
||||
"version": "1.0.2",
|
||||
"version": "2.0.0",
|
||||
"description": "Observe any object's any change.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -13,6 +13,7 @@
|
|||
"obaa",
|
||||
"omi",
|
||||
"observe",
|
||||
"watch",
|
||||
"observable"
|
||||
],
|
||||
"author": "dntzhang",
|
||||
|
@ -20,5 +21,5 @@
|
|||
"bugs": {
|
||||
"url": "https://github.com/Tencent/omi/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Tencent/omi#readme"
|
||||
"homepage": "https://github.com/Tencent/omi/tree/master/packages/obaa"
|
||||
}
|
||||
|
|
|
@ -3,33 +3,38 @@
|
|||
|
||||
<head>
|
||||
<title>obaa test</title>
|
||||
<script src="deep-equal.js"></script>
|
||||
<script src="util.js"></script>
|
||||
<script src="../index.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a href="https://github.com/Tencent/omi" target="_blank" style="position:absolute;right:0;top:0;">
|
||||
<img src="http://alloyteam.github.io/github.png" alt="" />
|
||||
</a>
|
||||
|
||||
<script>
|
||||
var result = [
|
||||
['a', 2, 1, '#'],
|
||||
['a', { b: 1 }, 2, '#'],
|
||||
|
||||
['a', { b: 1 }, 1, '#'],
|
||||
['b', 'abc', 1, '#-a'],
|
||||
['c', 3, undefined, '#'],
|
||||
['c', 4, 3, '#'],
|
||||
[1, 3, 2, '#']
|
||||
['d', 5, undefined, '#'],
|
||||
['h', {i:100}, undefined, '#-e'],
|
||||
['i', 22, 100, '#-e-h'],
|
||||
["1" ,3, 2, "#"]
|
||||
]
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
var count = 0
|
||||
var obj = { a: 1 }
|
||||
var obj = { a: 1, e: { f: { g: 2 } } }
|
||||
obaa(obj, function (prop, value, old, path) {
|
||||
count++
|
||||
var item = result.shift()
|
||||
console.log(deepEqual(prop, item[0]), deepEqual(value, item[1]), deepEqual(old, item[2]), deepEqual(path, item[3]))
|
||||
log(deepEqual(prop, item[0]), deepEqual(value, item[1]), deepEqual(old, item[2]), deepEqual(path, item[3]))
|
||||
})
|
||||
|
||||
|
||||
obj.a = 2
|
||||
obj.a = { b: 1 }
|
||||
obj.a.b = 'abc'
|
||||
|
||||
|
@ -38,29 +43,37 @@
|
|||
obaa.set(obj, 'c', 3);
|
||||
obj.c = 4;//c__4__3__#
|
||||
|
||||
console.log(count === 4)
|
||||
|
||||
obaa.add(obj, 'd');
|
||||
obj.d = 5;//c__4__3__#
|
||||
|
||||
obaa.set(obj.e, 'h', {i:100})
|
||||
obj.e.h.i = 22
|
||||
|
||||
log(count === 7)
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var arr = [1, 2]
|
||||
obaa(arr, function (prop, value, old, path) {
|
||||
count++
|
||||
if (count === 6) {
|
||||
console.log(deepEqual(prop, 'Array-push'), deepEqual(value[2], 'abc'), deepEqual(old, [1, 3]), deepEqual(path, '#'))
|
||||
} else if (count === 7) {
|
||||
|
||||
} else if (count === 8) {
|
||||
console.log(prop === 'newItem', value === 22, old === 11, path === '#-3')
|
||||
|
||||
} else if (count === 9) {
|
||||
console.log(prop === 'Array-size', value.length === 2, old.length === 4, path === '#')
|
||||
|
||||
if (count === 9) {
|
||||
log(deepEqual(prop, 'Array-push'), deepEqual(value[2], 'abc'), deepEqual(old, [1, 3]), deepEqual(path, '#'))
|
||||
} else if (count === 10) {
|
||||
|
||||
} else if (count === 11) {
|
||||
log(prop === 'newItem', value === 22, old === 11, path === '#-3')
|
||||
|
||||
} else if (count === 12) {
|
||||
log(prop === 'Array-size', value.length === 2, old.length === 4, path === '#')
|
||||
|
||||
} else if (count === 13) {
|
||||
//console.log(prop, value, old, path)
|
||||
|
||||
|
||||
} else {
|
||||
} else if(count==8){
|
||||
var item = result.shift()
|
||||
console.log(deepEqual(prop, item[0]), deepEqual(value, item[1]), deepEqual(old, item[2]), deepEqual(path, item[3]))
|
||||
log(deepEqual(prop, item[0]), deepEqual(value, item[1]), deepEqual(old, item[2]), deepEqual(path, item[3]))
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -82,10 +95,39 @@
|
|||
arr.push(111)
|
||||
//will not trigger callback
|
||||
arr.purePush(111)
|
||||
console.log(count === 10)
|
||||
log(count === 13)
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var countB = 0
|
||||
var objB = { a: 1, b: 2 }
|
||||
obaa(objB, ['b'], function (prop, value, old, path) {
|
||||
countB++
|
||||
log(prop == 'b', value == 3, old == 2, path == '#')
|
||||
})
|
||||
|
||||
objB.a = 2
|
||||
objB.b = 2
|
||||
objB.b = 3
|
||||
|
||||
log(countB === 1)
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<script>
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://github.com/Tencent/omi" target="_blank" style="position:absolute;right:0;top:0;">
|
||||
<img src="http://alloyteam.github.io/github.png" alt="" />
|
||||
</a>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
|
@ -87,3 +87,16 @@
|
|||
}
|
||||
|
||||
window.deepEqual = deepEqual
|
||||
|
||||
|
||||
function log() {
|
||||
for (var i = 0, len = arguments.length; i < len; i++) {
|
||||
if (!arguments[i]) {
|
||||
console.error.apply(null, arguments)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
console.log.apply(null, arguments)
|
||||
}
|
Loading…
Reference in New Issue