November 09, 2004
When this isn't this...
One of the privileges I have at my new job is being able to teach Flash training classes once a week. This week's class will finish up functions and elaborate on variable scope and event handling. We'll also build a first "real" application from the ground up. In preparing my notes, I was reminded of a problem that confuses a lot of new users still trying to grasp scope...
When Flash MX came out (years ago) a brilliant consequence was realized over Flash 5 - all of your code could now be placed on frames. It seems like such a simple concept, but it was brand new at the time. Sure Flash 5 had frame actions, but the Player had been updated to allow event handling code to be placed on a frame instead of requiring the code to be placed on a specific MovieClip or Button instances. This allowed for complete separation of ActionScript from source .fla files which resulted in a major boost in workflow. No more did we have to spend hours trying to find the nested movieclip 6 levels deep that contained the event handling code we sought. Double click the wrong thing when you're nested that deep and you lose your actions.. good luck finding them again, especially if you have the classic "blank" mc on the stage just to use it's enterFrame event.
For me, the change was welcome. I've always been heavy on the coder side and having large script blocks fit my mental model perfectly. The change was easy, too. We just replaced actions on MovieClips (like, onClipEvent(enterFrame) { ... }) with actions on Frames (movieClipInstance.onEnterFrame = function() {.. }). Awesome.. Everything was the same, worked just as well, and it was easier to write code and maintain it. Definitely a win - I couldn't imagine going back to placing event handling code on individual instances. Can you?
In the change, however, a subtle shift in scope occurred on Buttons. Consider the following code:
// button on the stage "aButton" // actions on the button look like this, obviously not commented out though /* on (press) { trace(this.prop); } */ // actions on the frame are below: prop = "timeline property"; aButton.prop = "button property"; // the on press is a button event aButton.onRelease = function() { trace(this.prop); }
The code is simple. We create a variable on the timeline called "prop." We also create a variable with the same name inside of the button instance. The button then has a press event on the button itself, and a release event that contains the exact same code but defined as an event handler property on the frame containing the button.
When the code executes, if you press the button you see "timeline property" in the output. Likewise, if you release the button you see "button property." What happened? If the code in the event handler is the exact same, shouldn't we see the same thing? The simple answer is "this" isn't what we expected it to be. When you define event handlers in frame actions, "this" is scoped to the object containing the listener that receives the event (there are expections, of course). However, if you use an event handler on a button itself, "this" actually refers to the timeline the button resides on. In this example, the aButton.onRelease has "this" as the button instance, but the on (press) has "this" as the timeline the button lives on. Crazy, huh?
I don't believe this is too much of a problem anymore today since most people I come in contact with no longer rely on the "Flash 5" style of event handling. However, the button and movieclip event handlers have not been deprecated, so the chance to make this mistake still exists. If you use button event handlers I'm sure you're already aware of this. If you don't use button event handlers, now is not a good time to start.
I know this is old news to a lot of you.. but again, I was thinking about this today as I was preparing my notes for tomorrow's class. I thought I'd share this in the chance that this entry might lead someone to greater understanding.

Comments
"this" gets even a more wonky when you start assigning references to functions for the event handlers instead of anonymous functions...
Posted by: flashape at November 9, 2004 08:29 PM
Even wierder, try:
aButton.onRelease = function() {
trace(prop);
trace(this.prop);
}
And you'll get:
timeline property
button property
So (prop != this.prop) !!
Which is different from all other object-oriented programming languages. This is what confused me for a while when i first started learning.
Posted by: dave at November 9, 2004 09:47 PM
From what I've read up on this, the issue is Buttons exclusively. If you want to really see it, drop two button instances on a frame, and throw an on(release) onto one of them, and set the other's onRelease on the frame - just have the body of both be trace( this ). The one that's set directly on the button will trace it's parent (_level0, in this case), while the other will trace the instance name (_level0.instance1 or _level0.instance2, assuming you haven't named the buttons explicitly).
As for the prop != this.prop, that's a function scoping issue - namely, that if a local variable (and anything defined on a timeline is in effect a local variable) has the same name as an instance property ( eg: var _x = 12; function tester() { trace( _x + " != " + this._x ); } ), the local variable is used.
Which, in some ways, makes sense. For instance, with function arguments...
Though it can certainly trip you up. ;)
Posted by: Jason Nussbaum at November 10, 2004 04:46 PM