Skip to content

Instantly share code, notes, and snippets.

@eligrey
Created April 30, 2010 01:38
Star You must be signed in to star a gist
Save eligrey/384583 to your computer and use it in GitHub Desktop.
object.watch polyfill in ES5
/*
* object.watch polyfill
*
* 2012-04-03
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
// object.watch
if (!Object.prototype.watch) {
Object.defineProperty(Object.prototype, "watch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop, handler) {
var
oldval = this[prop]
, newval = oldval
, getter = function () {
return newval;
}
, setter = function (val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
}
;
if (delete this[prop]) { // can't watch constants
Object.defineProperty(this, prop, {
get: getter
, set: setter
, enumerable: true
, configurable: true
});
}
}
});
}
// object.unwatch
if (!Object.prototype.unwatch) {
Object.defineProperty(Object.prototype, "unwatch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop) {
var val = this[prop];
delete this[prop]; // remove accessors
this[prop] = val;
}
});
}
@rokobuljan
Copy link

rokobuljan commented Nov 10, 2016

Seems there's an issue undefined on the oldval

fiddle demo

var myObject = {test:"a"};

myObject .watch("test", function (id, oldval, newval) {
console.log(oldval+' '+ newval);
});

myObject .test = "b"; // "a b"
myObject .test = "c"; // "undefined c"

@Swivelgames
Copy link

@rokobuljan That's not an issue... You're not returning the desired new value in the watch.

var myObject = {test:"a"};

myObject .watch("test", function (id, oldval, newval) {
    console.log(oldval+' '+ newval);
    return newval;
});

myObject .test = "b"; // "a b"
myObject .test = "c"; // "b c"

@mmtftr
Copy link

mmtftr commented Jan 31, 2017

Hey man! I made an awesome popup blocker with your snippet. https://gist.github.com/MMDF/d793c6dced90a41495a1dd61d6bf2f5b
Take a look and add it to Tampermonkey to use it. Thanks for the public snippet!

@alierdogan7
Copy link

This was a day saver. Thanks for your nice snippet :)

@georgir
Copy link

georgir commented May 15, 2017

A more generic version is possible. Instead of getting directly this[prop] at the start, the watch function should try to getOwnPropertyDescriptor on the object or along its prototype chain until it succeeds, then handle accessor properties by invoking the original getter/setter. This way you can watch html input elements "value" and "checked" properties for programmatic modification for example.

@mategvo
Copy link

mategvo commented Jul 28, 2017

Has anyone developed a version that would track children properties too?

@Erutan409
Copy link

@mateuszgwozdz Vue.js can do it.

@Erutan409
Copy link

@georgir What are you referring to, exactly? Can you provide an example?

@chack1172
Copy link

Is it possible to watch window and check when variable is set with var el?

@BSBProphet
Copy link

value: function( prop, handler ){
var oldval = this[prop],
getter = () => ( oldval ),
setter = function( val ){
if ( oldval != val ){ // event with a different value
handler.call( this, prop, oldval, val);
return ( oldval = val );
}
};

@rubenreyes2000
Copy link

rubenreyes2000 commented Apr 18, 2020

@stefek99 : the handler function must return the new value

alternatively, if you want a behavior more akin watchers in vue/react/angular you can modify the setter function as follows:

setter = function (val) {
	oldval = newval;
	return newval = (function() {
		handler.call(this, prop, oldval, val);
		return val;
	})();
};

if forces the new value to always be passed and assigned to the property, and the handler function becomes just "notified" of the change

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