Mike Chambers

code = joy

Functions, activation objects and ‘this’ in ActionScript 3

with 14 comments

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.

Written by mikechambers

October 8th, 2008 at 12:10 pm

Posted in General

14 Responses to 'Functions, activation objects and ‘this’ in ActionScript 3'

Subscribe to comments with RSS or TrackBack to 'Functions, activation objects and ‘this’ in ActionScript 3'.

  1. “this” is why I welcomed AS3. I love the ability to easily call other methods of the current Class within local functions without needing a reference to the current Class. Anyway you look at it, “this” now makes ActionScript 3 easier to use in my opinion.

    Keith H

    8 Oct 08 at 1:47 pm

  2. It’s also worth noting that if you call apply() on a Function object, you can override its scope to change what “this” means.

    Josh Tynjala

    8 Oct 08 at 2:18 pm

  3. Such a simple thing to do “var objThis = this; function(){ trace(objThis); }”, but I remember discovering ‘this’ being single biggest problem I had developing flash back in … flash 5 I think? “this” issue has been around long before AS3.

    JTtheGeek

    8 Oct 08 at 2:34 pm

  4. I have also been re-reading EAS3 and I know taking my time through this part has helped me a lot in understand objects and their scope. This is a crucial point that a lot of developers are never really informed about directly. Hopefully this post will start to clear up some of the confusion a bit.

    Nice work, Mike.

    Kevin Suttle

    8 Oct 08 at 3:29 pm

  5. I also ran into this problem in AS2. I eneded up putting something similar to var scope:Object = this; at the top of all my Class methods. What’s changed with AS3?

    Darren

    8 Oct 08 at 3:42 pm

  6. Best is to avoid function closures whenever possible. Contrary to Javascript they are a quite a performance retarder too. Apart from that I LOVE that methods are bound to their instance’s scope now: makes a coder’s life so much more easier (never ever Delegate anymore!) :-) Still the internally created method closures remain a mystery to me..

    Sev

    8 Oct 08 at 4:30 pm

  7. Nice explanation, Mike. I notice that in your example you made use of a class declared outside of your main package. I have used that approach many times in my current AS3 project.

    However, today I stumbled upon a strange error related to their use. If you take this same code, and make FunctionClosureTest implement any interface, you will get compile errors in Flash CS4!

    I am hoping this is just a bug in the beta version I was using, but if you try this code, I think you’ll see the same results I did.


    package {
    import flash.display.Sprite;
    import ITest;

    public class FunctionClosureTest extends Sprite implements ITest
    {
    public function FunctionClosureTest()
    {
    trace("Hello world");
    }
    public function method1():void
    {
    trace("method1");
    }
    }
    }
    class Foo
    {
    public function runFunction(f:Function):void
    {
    f();
    }
    }

    And elsewhere define the interface…

    package {
    public interface ITest
    {
    function method1():void
    }
    }

    CS4 will tell you that you have not implemented the interface appropriately, although the required methods are there. Even stranger still, the solution is to add another interface!

    The following works just fine:

    public class FunctionClosureTest extends Sprite implements ITest, IFixTheProblem

    Chris Deely

    8 Oct 08 at 7:36 pm

  8. Unless I’m mistaken, you have your terminology slightly confused. The closure object is a transparent object that wraps function references and maintains a pointer back to the scope it was created in. This only applies to named / non-dynamic functions. Closure objects are new in AS3, and are the reason that the Delegate class is no longer needed for things like event listeners (because the method is wrapped in a CO, and is called in its original scope).

    What you are referring to above is the “activation object” of anonymous functions. The activation object holds the local variables that were defined when the anonymous function was instantiated. Activation objects have been around since AS1.

    It’s also worth noting that in almost every case, the use of an anonymous function in your code indicates an architectural problem. There are almost no real uses for anonymous functions – they are less efficient, much harder to debug, and far harder to grok when reading code.

    Grant Skinner

    9 Oct 08 at 8:52 am

  9. this is definitely an old tric i’ve used since as1 and still use for javascript when needed. But i would agree with Grant, it’s usually quite avoidable, although I tend to use it for my inline setInterval functions and setTimeout stuff, javascript obviously

    Chris

    9 Oct 08 at 11:10 am

  10. @grant

    Good catch (I updated the post).

    Here is the info from the docs:
    http://livedocs.adobe.com/flex/3/html/help.html?content=03_Language_and_Syntax_21.html

    Activation Object is an object that “is created that stores the parameters and any local variables or functions declared in the function body. ”

    “A function closure is an object that contains a snapshot of a function and its lexical environment. A function’s lexical environment includes all the variables, properties, methods, and objects in the function’s scope chain, along with their values. Function closures are created any time a function is executed apart from an object or a class.”

    mike chambers

    mesh@adobe.com

    mikechambers

    9 Oct 08 at 11:58 am

  11. Grant, I am a bit confused. When you state that the use of anonymous functions within code can indicate an architectural problem I would take that at near gospel truth – as you are the industry leader. (sometimes I wonder if Grant Skinner is a pseudonym for 10 Superman type flash guys – just kidding).

    but I came across this post that uses an anonymous event handler function as part of a function literal within a function closure in order to “pass arguments to an event handler”
    http://www.transcendentaxis.com/dthompson/blog/archives/19#more-19

    Would you say that this could be an acceptable use case of an anonymous function?

    Evan

    4 Feb 09 at 9:46 am

  12. I wouldn’t take that literally. Usage of nameless functions is not indicative of bad architecture right off the bat. But it is best not to use them in application code to avoid memory leaks and not to design APIs that rely on them. When used carefully they can help you create some very powerful, higher level mechanisms inside frameworks.

    Kirill Mourzenko

    14 May 10 at 7:25 am

  13. [...] You can read more about lexical scoping, closures, and all the other Functional stuff if you want, but I wanted to focus on Scope because it’s bloody AS1′s thisThing.owner/mx.utils.Delegate all over again. If you don’t remember what I’m talking about, or you have no context, Keith talks about it and how to get around ActionScript 1′s scoping challenges via the Activation Object and Mike Chambers elaborates on how closures work in AS3. [...]

  14. [...] problem…?” in his/her head. Specifically all the scope issues we had in AS1/AS2 using Activation Objects, and Delegate. Functional afficiandos’ think it’s neat… until they have a [...]

Leave a Reply