mike chambers | about

Functions, activation objects and ‘this’ in ActionScript 3

Wednesday, October 8, 2008

I have been reading through Colin Moock’s Essential ActionScript 3 book, taking my time on each chapter to make sure I get the most out of it. I have been using ActionScript 3 pretty consistently for a couple of years (about a year before it was public), but I have been pleasantly surprised with how much stuff I am learning from reading Moock’s book.

Anyways, I am currently reading Chapter 5 on Functions which among other things covers function scope and closures activation objects (although the book goes into more detail in Chapter 16). This is one of the things that frequently trips up new developers. When creating a function, the function has access to all of the variables within its scope, even if the function is passed to and called within another scope. However, one thing to watch out for is that when using function closures, this always points to the Global object, and not to the object / instance that the function is called within.

For example:

FunctionClosureTest.as

package {
    import flash.display.Sprite;

    public class FunctionClosureTest extends Sprite
    {
        public function FunctionClosureTest()
        {
            
            var f:Function = function():void
            {
                trace(this);
            }
            
            f();//call function
            trace(this);//trace current scope
            
            var k:Foo = new Foo();
            k.runFunction(f);//call function in different instance
        }
        
        public override function toString():String
        {
            return "FunctionClosureTest";    
        }
    }
}


class Foo
{
    public function runFunction(f:Function):void
    {
        f();
    }
}

//rewrite toString for Global object
this.toString = function():String
{
    return "Global"
}

Outputs:

    Global
    FunctionClosureTest
    Global

However, this doesn’t mean that we cant access the original scope that this points to. We just need to copy that to the local scope in a variable, so it is stored and accessible within the function closure.

Here is the updated example:

FunctionClosureTest.as

package {
    import flash.display.Sprite;

    public class FunctionClosureTest extends Sprite
    {
        public function FunctionClosureTest()
        {
            var scope:Object = this;
            var f:Function = function():void
            {
                trace(scope);
            }
            
            f();//call function
            trace(this);//trace current scope
            
            var k:Foo = new Foo();
            k.runFunction(f);//call function in different instance
        }
        
        public override function toString():String
        {
            return "FunctionClosureTest";    
        }
    }
}


class Foo
{
    public function runFunction(f:Function):void
    {
        f();
    }
}

//rewrite toString for Global object
this.toString = function():String
{
    return "Global"
}

The only change is to store a reference to the current this in a variable, and then access that variable from the function closure activation object.

var scope:Object = this;
var f:Function = function():void
{
    trace(scope);
}

This now outputs:

FunctionClosureTest
FunctionClosureTest
FunctionClosureTest

By storing the this reference in a local variable, we are able to then access it later from the function closure activation object, regardless of which scope the function is called within.

twitter github flickr behance