The Object.watch method

The Object.watch, an often overlooked method, is quite powerful and well worth educating yourself on. If you have ever wanted to know every time a value changes and take actions accordingly, this is the function for you. The Object.watch method allows you to take a programmer defined action every time a property is set. In fact, your action actually takes place in the limbo time between a property being assigned by a user and when it is initialized to that value. What does that mean, let's take a look. ...

First off the Object.watch method is called like this:

Object.watch( property:String, handler:Function[, userData:Object] ):Boolean

The first parameter, 'property', is the property of the Object you want watched. So if your Object has a property called color then your property would be "color" ( make sure it is a String ).
The second parameter, 'handler' is a Function object. The handler Function is the function that takes action when a property gets assigned a value.
The third argument, 'userData', is just an optional piece of data you can pass into the handler function. You only get one, so if you want to pass in more than one piece of data you will have to package them up in an array or an Object.

Now let's take a look at some sample code:

// aButton is a Button component placed on the stage.var aButton:Button;
var obj:Object = new Object();
obj.color = "blue";
obj.watch( "color", watcher, this );
function watcher( prop, oldValue, newValue, userData )
{
  trace( prop + " changed from " + oldValue + " to " + newValue );
  trace( "userData = " + userData );
  trace( "obj.color( in watcher ) = " + obj.color );
  return newValue;
  //return oldValue;
  //return;
}

aButton.onRelease = function( )
{
  this._parent.obj.color = "red";
  trace( "obj.color( after assignment ) = " + this._parent.obj.color );
}

This allows you to see it in action. You could of course get 'obj.color' to be assigned many different ways, but this will do for our purposes. What I want you to look at here is the function 'watcher'. Whatever function you create must follow this same definition. All 'watcher' functions should have a the arguments, 'prop(erty)', 'oldValue' and 'newValue' - and in that order. The fourth argument is the optional userData you may have passed in.

What you really get to see in the previous example is what the current value of the property being watched ( obj.color ) is during the function. You also get to see how changing the return value of your handler function affects the properties value after the assignment occurs. You can keep a property from ever being changed by having a simple handler/watcher function that just returns the old value.

Now, how would you use this? The first thing that may come to mind is to watch changes in the properties of MovieClips( _x, _y, _width, _height, etc. ). That is probably the most useful way to use the Object.watch method, however, those properties are actually cleverly disguised getter/setter functions. Getter/setter properties can not be watched with Object.watch(). Don't throw the idea away though. You can use a proxy object to pass the information to the real MovieClip.

For example:

var mc:MovieClip; // some MovieClip you would like to watch.
var proxy:Object = new Object();
proxy._x = 0;
proxy._y = 0;
proxy._width = 100;
proxy._height = 100;
for( var x:String in proxy )
{
  proxy.watch( x, watcher, mc );
}

function watcher( prop:String, oldVal, newVal, clip:MovieClip )
{
  clip[prop] = newVal;
  trace( prop + " changed from " + oldVal + " to " + newVal + " on proxy and mc" );
  return newVal
}

Use this code as the basic idea behind using a proxy to use Object.watch with MovieClip properties. Just assign the value to the proxy instead of directly to the MovieClip and you can used that value as it is being changed in the MovieClip instance. If you actually need a true MovieClip proxy you can just extend the MovieClip class. A basic MovieClipProxy class is available over at senocular.com. In it's current form it doesn't do anything except get the values to and from the original when you use the proxy. You might also want to change the way the constructor works to suit your needs.

On a final note, the yang to the ying that is the Object.watch method is the Object.unwatch method. Once you no longer want to watch a property you can stop by calling Object.unwatch( property:String ).