August 25, 2008 Making Anonymous Functions and Closures Work in ActionScript 2.0

One of the most powerful idioms available in JavaScript is the anonymous function closure: e.g. (function(){})();. If you are unfamiliar with this, it simply creates an anonymous scope bubble that can be used to prevent automatic global variables, or trick JavaScript into allowing private variables. Unfortunately, this idiom isnt available in ActionScript 2.0. It simply doesnt work. (As of ActionScript 3.0, it works perfectly.)

This means that, on the surface, the only way to prevent time line variables from being constantly created is an init function of sorts. (ActionScript 2.0 has a pseudo global object which is the main time line of a movie level, and a totally global object named _global.) This also means that private variables are restricted to the creation of object classes. I personally get annoyed at times with classes in ActionScript. One of the things that has always bugged me with ActionScript 2.0 and the introduction of classes is that they are hard to make portable. (I work on no less than 3 computers in a single week, and 4-5 when on business trips, so portability is paramount to my work.) Rather than being able to simply include the classes in the same folder as the flash working file (*.fla), you must specify where on the computer the includes are. This is just annoying if you work on the file on several different computers. There are a couple of ways to avoid naming the class include folder on each computer, but they are as much of a pain as the default method. Fortunately, there is a solution.

To reiterate, while this works in JavaScript and ActionScript 3.0, it will not in ActionScript 2.0:

  1. (function(){
  2. //statements
  3. })();
  4.  
  5. var myFunc = (function(){
  6. var hiddenVar = "hidden";
  7. return function()
  8. {
  9. return hiddenVar;
  10. }
  11. })();
  12. hiddenVar //undefined
  13. myFunc.hiddenVar; //undefined
  14. myFunc(); //returns "hidden"

The good news about ActionScript 2.0 is that it retains the ability to pass anonymous functions as variables to another function. Like so:

  1. var myArray = [5,1,6,23,15];
  2. var sortedArray = myArray.sort(function(a, b){
  3. return a - b;
  4. });
  5. trace(sortedArray); //returns [1, 5, 6, 15, 23]

Knowing this, we can emulate anonymous function closures in ActionScipt 2.0. First we will create a global function that returns the function passed to it.

  1. _global.closure = function(f)
  2. {
  3. return f;
  4. }

To use it, you treat it the same as you would the parenteses in the javascript idiom:

  1. closure(function(){
  2. //statements
  3. })();

Changing the name to something shorter will make this more practical and the code a little cleaner.

  1. _global.$ = function(f)
  2. {
  3. return f;
  4. }
  5.  
  6. $(function(){
  7. //statements
  8. })();

Given this new ability, we can now apply anonymous closures to our script without littering the time line object with useless variables.

Lets use a hypothetical situation to illustrate how we might use the anonymous closure. In this script, we want to make 5 buttons that each need to trace their respective number when you click them. We could code each button individually, but that is messy and doesn't promote good code reuse. Instead, lets make a loop that makes all the buttons work, in addition to making it easier to add more buttons.

  1. for (var i = 1; i <= 5; i++)
  2. {
  3. //for each button
  4. this["click" + i + "_btn"].onPress = $(function(n){
  5. //return a function that has access to n via
  6. // a closure
  7. return function()
  8. {
  9. //trace the number passed to the parent closure
  10. trace(n);
  11. }
  12. })(i); //pass in current loop iteration for caching
  13. }

Now when you press click3_btn, it will trace '3' and click5_btn, '5'. In this way, we avoid creating useless variables inside each button, and we didn't have to create a singular use function.

Earlier I mentioned my slight distaste for how classes were implemented in ActionScript 2.0. Let me first caveat this statement by saying they are extremely useful and are wonderful for individuals or teams that have good version networks and servers. Just for myself and the smaller applications I write, I prefer portable objects. Lets see how we would write a singleton object with private variables and functions:

  1. var bob = $(function ()
  2. {
  3. //---------------------------
  4. //private variables
  5. var secret = "you found me";
  6. //---------------------------
  7. //private functions
  8. function compare(a, b)
  9. {
  10. return a > b;
  11. }
  12. //---------------------------
  13. //constructor
  14. var Bob = {
  15. //---------------------------
  16. //public members/variables
  17. works :true,
  18. getSecret :function()
  19. {
  20. return secret;
  21. },
  22. getCompare :function(a, b)
  23. {
  24. return compare(a, b);
  25. }
  26. };
  27. //---------------------------
  28. //return object
  29. return Bob;
  30. })();
  31. //test
  32. trace(typeof bob) //returns object
  33. trace(bob.works); //returns true
  34. trace(bob.getSecret()); //returns the string: you found me
  35. trace(bob.secret); //returns undefined
  36. trace(bob.getCompare(1, 2)); //returns false
  37. trace(bob.compare(1, 2)); //returns undefined function

As you can see with the tests, we have a singleton object with private functions and variables as well as public methods and members. With a little work, using the prototypal inheritance method, we can make this a reusable pseudo class:

  1. var Bob = $(function ()
  2. {
  3. //---------------------------
  4. //private variables
  5. var secret = "you found me";
  6. //---------------------------
  7. //private functions
  8. function compare(a, b)
  9. {
  10. return a > b;
  11. }
  12. //---------------------------
  13. //constructor
  14. var Bob = function ()
  15. {
  16. //---------------------------
  17. //public members/variables
  18. this.works = true;
  19. };
  20. //---------------------------
  21. //public methods
  22. Bob.prototype.getSecret = function()
  23. {
  24. return secret;
  25. };
  26. Bob.prototype.getCompare = function(a, b)
  27. {
  28. return compare(a, b);
  29. };
  30. //---------------------------
  31. //return constructor
  32. return Bob;
  33. })();
  34. //invoke
  35. var mybob = new Bob();
  36. //test
  37. trace(typeof Bob); //returns function
  38. trace(typeof mybob); //returns object
  39. trace(mybob.works); //returns true
  40. trace(mybob.getSecret()); //returns the string: you found me
  41. trace(mybob.secret); //returns undefined
  42. trace(mybob.getCompare(1, 2)); //returns false
  43. trace(mybob.compare(1, 2)); //returns undefined

So, with a single global function, we can bring the magic of anonymous function closures to ActionScript 2.0. Enjoy!

AddThis Social Bookmark Button Posted by Greg Ferrell at 09:35 PM. Filed under: Flash-ActionScript • (0) CommentsPermalink