Skip to content

Instantly share code, notes, and snippets.

@sylvainpolletvillard
Created October 27, 2015 14:21
Show Gist options
  • Save sylvainpolletvillard/2310b297adb063c53be7 to your computer and use it in GitHub Desktop.
Save sylvainpolletvillard/2310b297adb063c53be7 to your computer and use it in GitHub Desktop.
Cant wait for Object.observe, so I wrote my own change detection system: simplistic, but I dont need anything else
(function (globals, factory) {
if (typeof define === 'function' && define.amd) define(factory); // AMD
else if (typeof exports === 'object') module.exports = factory(); // Node
else globals['observe'] = factory(); // globals
}(this, function () {
var ARRAY_MUTATOR_METHODS = ["pop", "push", "reverse", "shift", "sort", "splice", "unshift"];
return function observe(o, notify, signature) {
var observable,
isArray = Array.isArray(o) || o instanceof Array;
if(isArray){
observable = o.slice()
} else if (typeof o === 'function' || typeof o === 'object' && !!o){
observable = Object.create(Object.getPrototypeOf(o))
} else {
return o // supposed not observable
}
//TODO: use ES6 Proxy if available
function observeKey(key){
var keySignature = signature ? signature + "." + key : key,
propertyDescriptor = Object.getOwnPropertyDescriptor(o, key);
o[key] = observe(o[key], notify, keySignature);
if(propertyDescriptor && propertyDescriptor.configurable){
propertyDescriptor.get = function () { return o[key]; };
propertyDescriptor.set = function (val) {
o[key] = val;
notify("set", keySignature, val)
};
delete propertyDescriptor.value;
delete propertyDescriptor.writable;
Object.defineProperty(observable, key, propertyDescriptor)
}
}
if (isArray) {
ARRAY_MUTATOR_METHODS.forEach(function (method) {
Object.defineProperty(observable, method, { configurable: true, value: function () {
var start, nbToAdd, nbToRemove, returnValue, i;
switch(method){
case "pop": start = o.length-1; nbToRemove=1; nbToAdd=0; break;
case "push": start = o.length; nbToRemove=0; nbToAdd=arguments.length; break;
case "reverse": case "sort": start=0; nbToRemove=o.length; nbToAdd=o.length; break;
case "shift": start=0; nbToRemove=1; nbToAdd=0; break;
case "unshift": start=0; nbToRemove=0; nbToAdd=arguments.length; break;
case "splice": start=arguments[0]; nbToRemove=arguments[1]; nbToAdd=arguments.length-2; break;
}
returnValue = o[method].apply(o, arguments);
observable.length = o.length; // to fix holes if array has been shortened
for(i=0; i<nbToAdd; i++){
observeKey(start+i, notify, (signature||'')+'['+(start+i)+']')
}
notify("splice", signature, start, nbToRemove, nbToAdd);
return returnValue
}});
});
}
Object.getOwnPropertyNames(o).forEach(observeKey);
return observable
}
}));
@sylvainpolletvillard
Copy link
Author

Usage:

var obj = { x:41, y: { z: [] } };
var observed = observe(obj, function(){
   console.log([].join.call(arguments, ' ; '));
 });

observed.x++; // logs "set ; x ; 42"
observed.y.z.push("toto"); // logs "splice ; y.z ; 0 ; 0 ; 1"

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