ActionScript scoping


I recently ran into this crazy ActionScript scoping issue, that drove me so crazy I decided it needed a post. ActionScript is an ECMAScript language so learning it has been very fun. I’m able to use all my JS knowledge and then extend it to a much richer language.

In my test app, I’ve got a button, fTestButton, and a text field, fConsole.

If I have this in the actions panel:

fTestButton.onRelease = function() {
    trace("Button clicked, this = " + this + " and console = " + fConsole);
}

then when I click the button, I get:

Button clicked, this = _level0.fTestButton and console = _level0.fConsole

As best I can tell what’s happening is that fConsole’s scope is being evaluated at the time the function is created. This seems reasonable and consistent with everything I’ve read about ActionScript up until this point.

But if I then put this in an external class file, like this:

class Test {
    var mConsole;
    Test(t:MovieClip) {
        mConsole = t.fConsole;
        trace("console = " + mConsole);
        t.fTestButton.onRelease = function() {
          trace("Button clicked, this = " + this + " and console = " + mConsole);
        }
    }
}

and create it on the stage with

var foo = new Test(this);

Then my output is:

console = _level0.fConsole
Button clicked, this = _level0.fTestButton and console = undefined

The problem this exposes is that only the value of local variables is actually resolved in the event handler, or in any anonymous function declaration. Members of the containing class are not accessible.

The solution is make any member variables accessible within the current function’s scope so that when the event handler is declared, it resolves these values appropriately.

Here’s the fixed class:

class Test {
    var mConsole;
    Test(t:MovieClip) {
        mConsole = t.fConsole;
 
        // now store a local copy of this variable
        var console = mConsole;
 
        trace("console = " + mConsole);
        t.fTestButton.onRelease = function() {
          // reference the local console variable, rather than the member variable
          trace("Button clicked, this = " + this + " and console = " + console);
        }
    }
}

This unfortunately leads to some somewhat hacky code, if your event handlers need to access many internal methods or calls. One solution to this is just to declare a local reference to ‘this’ and throw it around inside your event handler.

Here’s another approach, using a local reference to ‘this':

class Test {
    var mConsole;
    Test(t:MovieClip) {
        mConsole = t.fConsole;
 
        // now store a local reference to 'this'
        var thisTest = mConsole;
 
        trace("console = " + mConsole);
        t.fTestButton.onRelease = function() {
          // reference the console through 'thisTest'
          trace("Button clicked, this = " + this + " and console = " + thisTest.mConsole);
        }
    }
}

Both ways work. The former leads to simpler code if you just need to access one variable within your anonymous function, but the latter is useful if you need access to many members, or need to call methods on ‘this’

Word has it Macromedia is releasing some kind of helper class to make this easier, but it is disappointing to me that ActionScript couldn’t be slightly more consistent about the way it resolves values inside of anonymous functions.

  1. #1 by iangreen on August 1, 2004 - 12:31 pm

    I got a good deal on Viag… err… nice post. answered my question off google:)

(will not be published)