views:

1534

answers:

5

Hi!

Is it possible to have an event that fires when the value of a certain variable changes? Thanks!

A: 

I guess you could use some getter and setter methods for the variable and fire the event there.

eKek0
+1  A: 

Not directly: you need a pair getter/setter with an "addListener/removeListener" interface of some sort... or an NPAPI plugin (but that's another story altogether).

jldupont
+8  A: 

No.

But, if it's really that important, you have 2 options (first is tested, second isn't):

First, use setters and getters, like so:

var myobj = {a : 1};

function create_gets_sets(obj) { // make this a framework/global function
    var proxy = {}
    for ( var i in obj ) {
        if (obj.hasOwnProperty(i)) {
            var k = i;
            proxy["set_"+i] = function (val) { this[k] = val; };
            proxy["get_"+i] = function ()    { return this[k]; };
        }
    }
    for (var i in proxy) {
        if (proxy.hasOwnProperty(i)) {
            obj[i] = proxy[i];
        }
    }
}

create_gets_sets(myobj);

then you can do something like:

function listen_to(obj, prop, handler) {
    var current_setter = obj["set_" + prop];
    var old_val = obj["get_" + prop]();
    obj["set_" + prop] = function(val) { current_setter.apply(obj, [old_val, val]); handler(val));
}

then set the listener like:

listen_to(myobj, "a", function(oldval, newval) {
    alert("old : " + oldval + " new : " + newval);
}

Second, I actually forgot, I'll submit while I think about it :)

EDIT: Oh, I remember :) You could put a watch on the value:

Given myobj above, with 'a' on it:

function watch(obj, prop, handler) { // make this a framework/global function
    var currval = obj[prop];
    function callback() {
        if (obj[prop] != currval) {
            var temp = currval;
            currval = obj[prop];
            handler(temp, currval);
        }
    }
    return callback;
}

var myhandler = function (oldval, newval) {
    //do something
};

var intervalH = setInterval(watch(myobj, "a", myhandler), 100);

myobj.set_a(2);
Luke Schafer
second is good:)
Sinan Y.
hehe, i'll remember eventually, for now i'm fixing code
Luke Schafer
fixed code, added my second idea
Luke Schafer
+1  A: 
Bruno Reis
+1 for encapsulation. That was my first thought, but I wanted the ability to add the create_gets_sets method eventually, and since it is indiscriminate, hiding the values isn't cool :) we can take it a step further and write some things to hide the values, but I think the code i've posted is confusing enough for most people... maybe if there's call for it...
Luke Schafer
+9  A: 

Yes, object.watch (it's non-standard though). Here's my implementation that works in every current major browser:

// object.watch
if (!Object.prototype.watch)
    Object.prototype.watch = function (prop, handler) {
     var val = this[prop],
     getter = function () {
      return val;
     },
     setter = function (newval) {
      return val = handler.call(this, prop, val, newval);
     };
     if (delete this[prop]) { // can't watch constants
      if (Object.defineProperty) // ECMAScript 5
       Object.defineProperty(this, prop, {
        get: getter,
        set: setter
       });
      else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
       Object.prototype.__defineGetter__.call(this, prop, getter);
       Object.prototype.__defineSetter__.call(this, prop, setter);
      }
     }
    };

// object.unwatch
if (!Object.prototype.unwatch)
    Object.prototype.unwatch = function (prop) {
     var val = this[prop];
     delete this[prop]; // remove accessors
     this[prop] = val;
    };
Eli Grey
I stand corrected. I take my had off to you sir.
Luke Schafer
This is an excellent method, however it appears unable to properly work in IE6. That may be a deal breaker for some.
Robotsu
This is great, but only works on firefox for me :(
Rodrigo
This snippet requires the handler to return the new value. Use val = newval;handler.call(this, prop, val, newval);in the new setter instead.
thSoft