Create a gist now

Instantly share code, notes, and snippets.

What would you like to do? polyfill in ES5
* polyfill
* 2012-04-03
* By Eli Grey,
* Public Domain.
if (! {
Object.defineProperty(Object.prototype, "watch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop, handler) {
oldval = this[prop]
, newval = oldval
, getter = function () {
return newval;
, setter = function (val) {
oldval = newval;
return newval =, 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;

Thanks for this snippet! It's way beyond my level of JS and incredibly useful.

I did have to modify it somewhat - the setter function is broken if the handler function doesn't return the new value. Took me a while, but I figured out how to fix it!

Tobriand, are you able to contribute the fix that you said you made, to the author for review and possible inclusion in the script. That could be helpful to others.

Thanks for building this. What license is it released under? (no license === All Rights least in the US). Thanks.


eligrey commented Oct 11, 2011


Cool. Would you mind adding the MIT header? Sadly, that's the only way it really "counts." Thanks.


eligrey commented Oct 11, 2011

I'd have a hard time in court proving that I didn't put this under the MIT license after the comment I just explicitly made.

I completely understand and agree. :) However, "corporate types" generally steer clear of any code that doesn't include both a copyright and a licensing claim (even if it's just "@license MIT" or some such). I'll likely use this in my own projects, regardless. :) Thanks for the license change. Making it more explicit may get it more usage, but that's completely your call.

In any case, I appreciate you licensing it and discussing this with me. Thanks, Eli.

There is a conflict with this script and Mootools that I can't seem to work out. I'm using MooTools Core 1.4.1 with MooTools More 1.4.01 (Specifically with Form.Request).

The error only happens in browsers that don't have native Specifically, the error is:

Uncaught TypeError Object (... the watch() function above ...) has no method include.

Here's an example of the code that kicks an error:

window.addEvent('domready', function(){'TestForm').formUpdate(null,{
    onComplete: function(target, tree, elements, html, javascript){

The conflicting code is in MooTools Core, on the this.Events.addEvent() function; excerpt below:

 this.$events[type] = (this.$events[type] || []).include(fn);

I'm guessing that the 'watch' function you've created is conflicting with a MooTools internal "watch" function, event, or other reserved word.

Seems that the culprit is a for in loop. Oddly enough, the object passed to that loop only has three members. However, the for loops through 5 members, the additional two being watch and unwatch. Example:

var events = {event1: function(),event2: function(), event3: function()};


for (var fn in events) console.log(fn);

Modifying the Object.prototype screws with for in loops. In the case of mootools, I'll be using Object.extend('watch',function(){}); instead of adjusting the prototype.

Also, for the time being, I've only found that this conflicts with Events.Pseudos of MooTools More. I've added the following code as a hack to get it functioning:'include',function(){return});

I've forked this for those that might want to use this with MooTools:

melanke commented Jan 17, 2012

thank you so much, Ive made a fork that could be useful:

take a look at the usage:

alert("some attribute of object has been changed");

"attr1", "attr2"
alert("attribute attr1 or attr2 of object has been changed");

object.watchOne("attr1", function(){
alert("attribute attr1 of object has been changed");

object.attr1 = "different value"; //will trigger a object.attr1 watch or object watchAll
object.attr1.push("new item"); //will trigger a object.attr1 watch or object watchAll

mspisars commented Apr 3, 2012

I keep getting a "Uncaught TypeError: Cannot call method 'call' of undefined" on the handler on line 12 in the code above.
Is this a know problem? Is there a way to work around it? I am on Chrome.


eligrey commented Apr 3, 2012

You need to specify a handler function. I'm guessing that you're only calling or the function you passed for the handler was null.


eligrey commented Apr 3, 2012

I just raised the minimum requirements by only supporting Object.defineProperty, as __define[GS]etter__-only browsers are now an ancient minority. @ericcholis: and object.unwatch are also unenumerable now so that bug won't happen anymore.

mspisars commented Apr 3, 2012

No, the error was on page load. The new code does not throw the error, i just have to make sure everything else works.
Thanks for the quick update!


eligrey commented Apr 3, 2012

The update had nothing to do with your issue, so you were probably calling too soon before a function was defined or something and later fixed it.

can you show some sample code on how to use this? thank you

rwaldron commented May 1, 2012

WTF with mixing comma first AND comma last in the same code?


eligrey commented May 1, 2012

@rwldrn Sorry, when I originally wrote this it was in comma-last form, and I forgot to completely update the code style in the recent update. I just fixed it.

Is IE8 still supported? When I try out a working page (in Safari, Chrome, ...) I get the following error:
"Message: Object doesn't support this property or method" on line 13.


eligrey commented Jun 8, 2012

Yes, but only for DOM objects.

paul-d commented Jun 19, 2012

This seems to be exactly what I was looking for, but will it work in all popular browsers/versions? My JS book doesn't even mention defineProperty.


eligrey commented Jun 19, 2012

It works with all current versions of all browsers. If you want to use it in IE8, it will only work on DOM objects.

paul-d commented Jun 19, 2012

Thank you. Apart from your solution, I haven't been able to find a way to tell if an object attribute changes, so I trust there is no solution that will work in browser versions in common use?

melanke commented Jun 19, 2012

@eligrey a time a go I was working on a fork of your object-watch, great job by the way, and got the same problem (obvious), today I am working on another library that handle this problem. Not with the same possibilities but it is nice too. Note that I even tested it yet, maybe tomorrow it will be finished :)
You can find it here:

Is there anything out there to watch all changes to properties of an object?

afraser commented Sep 28, 2012

This plus debugger; in Chrome = tasty.

ceanela commented Oct 9, 2012

ItsLeeOwen, I might be two months late, but maybe you should run a for loop to add watch to each property object. That seems the pretty quick and simple.

razola commented Dec 22, 2012

Thanks for this one!

Would calling unwatch from within the watch function for any reason potentially cause problems?

Hi there,
many thanks for this polyfill, but I've found a problem with it. It won't work for watching accessor properties (the ones with a get/set), while the original Mozilla's function does.
So, I've rolled my own version here:

is this compatible with harmony:observe?

Awesome thank you!

qhedges commented May 15, 2013

What if the property already has a getter and setter. Doesn't this override them?

juriseng commented Dec 4, 2013

Thanks so much, this snippet saved me! :)

jslegers commented Dec 7, 2013

Seems like a pretty elegant and optimal solution...

Nice job !

jslegers commented Dec 7, 2013

I tried the following example from :

var o = { p: 1 };"p", function (id, oldval, newval) {
    console.log( "o." + id + " changed from " + oldval + " to " + newval );
    return newval;

o.p = 2;
o.p = 3;
delete o.p;
o.p = 4;

o.p = 5;

The code breaks after the 'delete' statement. Firefox (using its native implementation) still triggers the 'watch' statement while Chrome (using the polyfill) doesn't. It's a minor issue, but I figured it's still worth mentioning...

When I remove the 'delete' statement, both both browsers are in synch.

dtpz commented Dec 8, 2013


Thanks, this is great - I created my own version after seeing yours here:

It takes what you did to the next level by allowing mutation of the set value (though this must be explicit), taking it off of the object prototype, and allowing multiple watchers and possibility to unwatch a specific function (the same way it's done with events).

Please tell me what you think - and thanks!

Zeokat commented Feb 24, 2014

Very usefull piece of code, you saved my day. Thanks.

Someone up above mentioned that the setter function does not work properly:

a = {b:"c", d:"e"}
   Object {b: "c", d: "e"}"b", function() { console.log("change, new a: ", a) } ) 

a.b = "new value of B"
   change, new a:  Object {d: "e", b: undefined}
   "new value of B"

   Object {d: "e", b: undefined}

UPDATE: 16 Sep 2015 - so happy that I've found my comment - the thing does not work for me. It wasn't working a year ago, it isn't working now.

Awesome, thank you!

@XoseLluis has proposed a great imporvement to this piece of code,
I've rewrote it, optimized for minification + included some tests :

The only limitation is, as @jslegers mentioned, that deleting a property remove the watchpoint !

koolb commented Aug 25, 2014

Thanks for this. Here's the coffee version (
if (not Object::watch?)
Object.defineProperty Object.prototype, "watch",
enumerable: false, configurable: true, writeable: false, value: (prop, handler) ->
oldval = @[prop]
newval = oldval
getter = () -> newval
setter = (val) -> oldval = newval; newval =, prop, oldval, val)
Object.defineProperty @, prop, get: getter, set: setter, enumerable: true, configurable: true if delete @[prop]

if (not Object::unwatch?)
Object.defineProperty Object.prototype, "unwatch",
enumerable: false, configurable: true, writeable: false, value: (prop) ->
prop = @[prop]
delete @[prop]
@[prop] = val

This is really nice! (comma first styling makes me want to kill my self though)

flackjap commented Nov 9, 2014

If anyone else comes by and needs a version of this that doesn't calls back when the newly set value of an object key is the same as previous, here's my fork:

I can't get this working with DOM Elements. e.g.

Am I doing something wrong?

I wanted to add a few keywords for people searching for this type of solution, I am using this with Node.JS / Express.JS and it works perfectly!


simple delete prop make it looks like not so perfect, if somewhere have call Object.defineProperty(target, targetProto) before watch , so the setter and getter (if exist) can't work

take a look at my modification, sorry for that i have not test it yet

and also sorry for unwatch implement


nigayo commented Jul 9, 2016

Thanks for this one.

rokobuljan commented Nov 10, 2016 edited

Seems there's an issue undefinedon 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"

@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"

MMDF commented Jan 31, 2017

Hey man! I made an awesome popup blocker with your snippet.
Take a look and add it to Tampermonkey to use it. Thanks for the public snippet!

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

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.

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

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