Skip to content

Instantly share code, notes, and snippets.

@w3core
Last active May 13, 2017 12:04
Show Gist options
  • Save w3core/0483c70491ae23f6b457811b16f9ae36 to your computer and use it in GitHub Desktop.
Save w3core/0483c70491ae23f6b457811b16f9ae36 to your computer and use it in GitHub Desktop.
Tiny crossbrowser recursive Object observer
/**
* Recursive Object observer for all existing or passed properties
*
* Notes:
* 1. You can use `delete` operator for any of children properties but not for the top level watchable properties from `object`
* 2. The watcher will be applied for all existing properties of `object`.
* To apply watcher for one property please pass `name` of property as argument.
*
* @param handler {Function} handler(operation, key, val, old)
* @param object {Object} Optional
* @param name {String} Optional. The `name` of property in `object` that to be handled
*
* @returns {Object}
*
* @license MIT
* @author Max Chuhryaev
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.$watch = factory());
}(this, (function () {
return $watch;
function $watch (handler, object, name) {
var handler = typeof handler == "function" ? handler : function(){},
object = object && typeof object == "object" ? object : {}
;
if (name && typeof name == "string" && typeof object[name] == "undefined") object[name] = undefined;
constructor(object, handler, undefined, name);
function constructor (object, handler, root, key) {
if (key && typeof key == "string") define(object, key, handler, root);
else Object.keys(object).forEach(function (key) {
define(object, key, handler, root);
});
}
function clone (o) {
if (o === null || typeof o !== "object") return o;
if (o instanceof Date) {
var c = new Date;
c.setTime(o.getTime());
}
else {
var c = o.constructor();
for (var i in o) c[i] = clone(o[i]);
}
return c;
}
function intercept (object, key, handler, root, before, after) {
if (before != null && typeof before == "object") {
for (var i in before) {
if (before.hasOwnProperty(i)) {
if (before[i] !== after[i] && typeof after[i] == "undefined") {
handler("delete", (root ? root + "." : "") + i, after[i], before[i]);
}
}
}
}
else if (before !== after && typeof after == "undefined") {
handler("delete", root, after, before);
}
if (after != null && typeof after == "object") {
for (var i in after) {
if (after.hasOwnProperty(i)) {
if (before[i] !== after[i] && typeof before[i] == "undefined") {
handler("add", (root ? root + "." : "") + i, after[i], before[i]);
define(object, i, handler, root);
}
}
}
}
else if (before !== after && typeof before == "undefined") {
handler("add", root, after, before);
debugger; //TODO: What is "i" and "root" in this case?
define(object, i, handler, root);
}
}
function define (object, key, handler, root) {
if (object.hasOwnProperty(key)) {
var value = object[key], $root = (root ? root + "." : "") + key;
function $value () {
var before = clone(value);
setTimeout(function () {
intercept(value, key, handler, $root, before, clone(value));
}, 0);
return value;
}
Object.defineProperty(object, key, {
enumerable: true,
get: function () {
return $value();
},
set: function (v){
var old = value;
value = v;
if (value && typeof value == "object") constructor(value, handler, $root);
handler("update", $root, value, old);
}
});
if (value && typeof value == "object") constructor(value, handler, $root);
}
}
return object;
}
})));
@w3core
Copy link
Author

w3core commented Oct 19, 2016

Usage example

var $model = {};
$model.$scope = {};

$watch(function(operation, key, val, old){
  console.log(operation, key, old, val);
}, $model, "$scope");

setTimeout(function(){
  console.warn("1. Add property")
  $model.$scope.a = "Add property";
}, 1000);

setTimeout(function(){
  console.warn("2. Change property");
  $model.$scope.a = {"Change":"Property"};
}, 2000);

setTimeout(function(){
  console.warn("3. Add subproperty");
  $model.$scope.a.subproperty = "Add subproperty";
}, 3000);

setTimeout(function(){
  console.warn("4. Change subproperty");
  $model.$scope.a.subproperty = {"Change":"Subproperty"};
}, 4000);

setTimeout(function(){
  console.warn("5. Delete property");
  delete $model.$scope.a;
}, 5000);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment