Scope Chain and Memory waste in Flash MX

- By Timothée Groleau -

First published on 8 April 2003

Introduction

Flash MX is very powerful. And what goes with it is that developer must be careful of what they are doing. Your script can have unexpected results or work but you don't really know why. In some cases a lot may be happening in the back of your script.

So today, I'll try to walk through the scope chain and show how scope chain in Flash MX can lead to memory waste. Although this paper starts really really basic, it is intended to people who have a rather good understanding of ActionScript. Many of the scripts mentionned here may be applicable to JavaScript but I haven't tested any of them in a browser so I don't garantee anything on that.

 

Translations

[Updated on 11 June 2003]

This page has be more popular than I thought and some very kind people have even translated it.

A japanese translation by Jun Kitazawa and Naohiko Ueno from Bascule Inc. is available http://faces.bascule.co.jp/scopechain.html.

A chinese translation by Justin, Lee Yi-Hsou is available at http://www.lis186.com/article/flash/scope_chain.htm.

I should probably do the French translation one of these days :p.

 

Acknowledgment

This article doesn't bring anything new to light. Rather it is a sort of summary of many threads that I have read on the FlashCoders' List where the people involved were mainly Tatsuo Kato, Casper Schuirink, Peter Hall, Ralf Bokelberg, Gregory Burtch as well as many others.

To my knowledge, the person who identified the scope chain pattern for nested functions is Naohiko Ueno on a Japanese mailing list. You can go to his post here (beware for reading the archives of that list, it is necessary to subscribe to the list). The information was subsquently brought to the FlashCoders' list by Tatsuo Kato.

Many of the examples in this article are taken straight from Tatsuo Kato's various posts and he is the great inspiration for this article.

 

A little reminder

On Memory management

You have probably noticed that, in ActionScript, you never need to allocate or free memory. That's because ActionScript is a language with a very high level of machine abstraction and handles the memory automaticaly for you.

A very popular term in memory management is "Garbage Collector". when you build variables and objects, Flash automatically allocates memory for them. The Garbage collector is a process which keeps tracks on whether objects are still being used by your program or not. If it detects that an object is no longer used, it will delete it and free the memory, releasing precious resources.

Garbage collectors are a full topic on their own, way beyond the scope of this paper, with techniques such as "reference count" and "mark and sweep" to keep track of which objects are still alive. In this paper, we are going to say that an object remains in memory as long as there is at least one reference to it in the main program or in one of the other objects currently in use by the main program.

 

On Functions

"(var) used to declare local variables. If you declare local variables inside a function, the variables are defined for the function and expire at the end of the function call."

That statement is an extract from the actionscript dictionnary for the entry var. Like it says: var is used to declare local variables in a function and these lcoal variables will be deleted when the function finishes executing. What happens is that the garbage collector will remove them because there will be no reference to them in the main program. Good for us!

A simple example:

Code:
1
2
3
4
5
6
test = function() {
  var a = 5;
  trace("inside: " + a);
}
test(); // inside: 5
trace("outside: " + a); // outside: 
Output:

inside: 5
outside:

I am sure you are all too familiar with this.

 

Now here is another interesting thing with functions, from inside the function body you can call a variable that is outside of the function like this:

Code:
1
2
3
4
5
a = 5;
test = function() {
  trace(a);
}
test(); // 5
Output:

5

"Yeah, normal!" you say? But what really did happen? How come Flash can access the variable "a" when it is outside the function body? The answer is that Flash followed the scope chain attached to the function.

 

What is the scope chain?

The term "scope chain" appears only in one place in the actionscript dictionary That is in the page for the action with. I recommend everybody to read that page because it's full of very precious information. That page, however, is a bit too fast for this paper so far, so let's slow down.

So, what is the scope? To me, the scope is the object Flash inspects when it looks for a variable. From this simple definition, the scope chain is a set of objects that Flash will inspect sequentially when it looks for a variable. During execution, Flash sees the scope chain as a simple stack, and will start searching from the top. If the object sitting on top of the scope chain does not contain the desired variable, Flash will move deeper to the next object and repeat the process until either the variable is found or all the objects in the scope chain have been inspected.

The scope chain is only looked-up to retrieve properties which are being accessed without explicit scope. For example, when doing "trace(a);", Flash looks for "a" in the scope chain because it is not explicitely stated where "a" must be located. When doing "trace(anyObject.a);", there is an explicit scope to search to find "a". The scope is specified by the "anyObject" reference so Flash will only look inside the object "anyObject" to look for "a" and not inside the scope chain.

In Flash, ActionScript code can only be written in a timeline, that is inside a movie clip (be it _root or any other movie clip). From the place the actionscript code is written, the scope chain contains at least 2 elements: the current object, which contains the code and the object _global. Below is a quick test script:

Code:
1
2
3
4
5
_global.a = 4;
a = 5;
trace(a); // 5
delete a;
trace(a); // 4
Output:

5
4

Here, just for the road, let's describe line by line what is happening: Line 1 creates a variable "a" in the object _global. Line 2 creates a variable "a" in the current scope. In line 3, to trace"a", Flash looks for "a" in the curent scope, finds it and outputs it (5). Line 4 deletes "a" from the current scope. In line 5, to trace"a", Flash looks in the current scope for "a", doesn't find it, move on to the second object in the scope chain, looks for "a", finds it and outputs it (4).

The reason the term "scope chain" appears in the page for the with action because with can be used to add an object on top of the scope chain.

Code:
1
2
3
4
5
6
7
8
9
10
11
_global.a = 4;
a = 5;
obj = new Object();
obj.a = 6;
with(obj) {
  trace(a); // 6
  delete a;
  trace(a); // 5
  delete a;
  trace(a); // 4
}
Output:

6
5
4

As you can see the same statement "trace(a);" called 3 times, has a different outputs each time, that's because we have been deleting the variable "a" from the succesive scopes so Flash had to look deeper and deeper in the scope chain to find a variable that matched the reference "a".

 

Function and activation object

Function creation

When a function is created, what Flash uses currently as the scope chain is attached to the function as the function's own scope chain.

Once attached to the function, the scope chain does not change and new objects cannot be added or deleted from it. For example:

Code:
1
2
3
4
5
6
7
8
9
10
11
a = 5;
test = function() {
  trace(a); // 5
  delete a;
  trace(a); // undefined
};

obj = new Object();
obj.a = 6;
obj.meth = test;
obj.meth();
Output:

5
undefined

Although "meth" is now a method of the object "obj", "obj" is NOT in the scope chain of "meth". It means the scope chain of "test" was not affected by the assignment to "obj". The only thing that would be affected from inside the function is the reference "this" (not used in the example above), more about the "this" reference later.

You may think the test above is not fair since the function is originaly created as "test", but that doesn't matter. The only thing that is important, is that the very specific piece of code "function() {...}" appears in the code block. So even without using a temporary variable, the result is the same:

Code:
1
2
3
4
5
6
7
8
9
a = 5;
obj = new Object();
obj.a = 6;
obj.meth = function() {
  trace(a); // 5
  delete a;
  trace(a); // undefined
};
obj.meth();
Output:

5
undefined

 

Function execution

Each time a function is executed, a new object is created transparently. That object holds all the local variables created with the keyword var, the function parameters and the arguments array. That object is called the activation object. The term "activation object" also apears in only one place in the actionscript dictionnary, which is again the entry for with.

When the function runs, the function's scope chain is used as the current scope chain and the activation object is placed on top of it. So from inside a function's body, when the script executes, the scope chain is as follow:

activation object -> function's scope chain

In the code above, if the function was created from the first frame of _root, the scope chain, when the function executes, is as follow:

activation object -> _root -> _global

 

As far as memory management goes, the concept of activation object also shed some light on what exactly happens when a function executes:

  1. The activation object is created
  2. All the local variables are created as properties of the activation object
  3. The code is executed with the activation object as the context object
  4. When the code finishes, since there is no link to the activation object anywhere in the program, the activation object, along with all its properties, is garbage collected and resources are freed.

 

Nested functions and memory waste

Introduction

We are finaly getting to the interesting part (sorry it took so long). With Flash MX, the new event model makes it very easy to assign function on the fly to any event handler (or any other variable for that matter). A logical thing that comes to mind is to create functions as wrapper to perform some actions AND attach other functions to event handlers.

For example:

Code:
1
2
3
4
5
6
7
8
9
resetMC = function(mc) {
  mc._x = mc._y = 0;
  mc.onEnterFrame = function() {
    this._x += 2;
    this._y += 2;
  }
}
resetMC(mc1);
resetMC(mc2);

The function "resetMC" takes a movie clip as a parameter, reset its location to (0,0) and assign a function to the onEnterFrame handler so that the movie can start moving diagonaly towards the bottom right. That seems reasonable. However, there is a catch.

 

Persistent activation object

Before we carry on, we have to introduce some simple terminology. Otherwise, it'll be to complicated to write down what is happening. Simply, when a function is created whithin another function, we are going to call the function within the inner function and the function outside the outer function.

So if you have understood all that we have said previously, you surely already know what is happening. In the code above, each time "resetMC" is called, an activation object for the current run is created and is added to the function's scope chain to form the current scope chain. When line 3 is reached, the inner function is created and is assigned, as the onEnterFrame handler, to the movie clip that was passed as a parameter.

When the inner function is created, the current scope chain will be attached to that function as its own scope chain. The thing is that the activation object of the outer function is part of the current scope chain, which means a reference is now kept in the scope chain of the inner function.

Typically, if the code above was written in the first frame of _root, the scope chain attached to the inner function would be:

activation object of the outer function -> _root -> _global

And when the inner function will run, later on, the scope chain will be:

activation object of the inner function -> activation object of the outer function -> _root -> _global

"What's the big deal?" you ask. Well, because the activation object of the outer function now is referenced in the scope chain of the inner function, and because the inner function is now persistent, as a method of the movie clip, the activation object itself has become persistent. Indeed, for the garbage collector, there is now one persistent reference to the activation object so it cannot remove it. Because of that, the activation object, along with all the local variables it carries will not be released from memory.

 

Function duplication

There are several implications of creating functions inside functions. Firstly, as we have mentionned before, a new activation object is being created each time a function runs. So, still referring to the code above, imagine we want to use the function "resetMC", to assign a onEnterFrame handler to 100 movie clips. Doing this will lead to having 100 different activation objects that will remain in memory.

Secondly, because the inner function is created and assigned in the outer function, each movie clip is assigned a different inner function object too, each taking its own memory slot. This is definitely not very efficient since the inner function does the same thing for every movie clip on which it is attached (the code is the same). By the way this is also the reason OOP recommends to make methods of the class available in the class prototype instead of from the constructor. If methods were created from the constructor, we would have duplicate functions for each instance of the class.

 

Memory waste, not memory leak

It's quite important to make a distinction between the two. A memory leak is a problem encountered when a program continuously increases the resources it uses, potentially leading to a system crash.

For the case of nested functions in Flash, what we have is NOT a memory leak, it's a memory waste. The inner function keeps a reference to the activation object of the outer function, but it's a one-to-one relationship. Once the inner function is removed from memory (for instance if the object to which the inner function was attached is removed), then the unique reference to the activation object also gets deleted and the garbage collector will (should) delete both the inner function and the activation object of the outer function at the same time. So the resources used to hold the activation object of the outer function are being wasted but do not increase and therefore, the memory usage cannot just keep growing out of control.

 

Can we proove all that?

On activation object persistence

Yes! Consider this piece of code:

Code:
1
2
3
4
5
6
7
8
9
test = function(obj) {
  var a = 5;
  obj.meth = function() {
    trace(a);
  }
}
o = new Object();
test(o);
o.meth(); // 5
Output:

5

In the function "test", "a" is a local variable so we would expect that after we call the function "test", "a" is destroyed and released from memory. However, when we run the code above, calling "meth" on the object "o" outputs "5", which means that "a" WAS found in the scope chain of "meth" and shows it had not been released from memory.

You may be thinking that "a" was found because there was a hardcoded reference to it in the inner function. Flash could have been doing "something" internaly to keep that particular reference alive. That would be nice but really, that is not the case. The activation object of the outer function WAS stored in the scope chain of the inner function and ALL the local variables can be retrieved.

Using eval, we can dynamically search for a variable in the scope chain. Look at the following piece of code:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
test = function(obj) {
  var aVariable_1 = "Hello";
  var aVariable_2 = "There";
  var aVariable_3 = "Tim";
  obj.retrieve = function(refName) {
    trace(eval(refName));
  }
}
o = new Object();
test(o);
o.retrieve("aVariable_1"); // Hello
o.retrieve("aVariable_2"); // There
o.retrieve("aVariable_3"); // Tim
Output:

Hello
There
Tim

In the code above, the function "retrieve" does not contain a hard-coded references to any variable, yet by passing the variable's name and using eval, we are able to retrieve all the local variables that we thought were deleted. Although these variables were not used in the program they remained in memory, wasting resources.

 

On duplication and memory waste

Yes again! Let's consider the following code:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
addFunc = function(obj) {
  var aVariable = new Object();
  aVariable.txt = "Hello there";

  obj.theFunc = function() {
    return aVariable;
  }
}

o1 = new Object();
o2 = new Object();

addFunc(o1);
addFunc(o2);

trace(o1.theFunc().txt); // Hello there
trace(o2.theFunc().txt); // Hello there

trace(o1.theFunc == o2.theFunc) ; // false
trace(o1.theFunc() == o2.theFunc()) ; // false
Output:

Hello there
Hello there
false
false

Line 19 shows that although the methods carried by "o1" and "o2" have the same name they do not reference the same function object in memory.

Line 20 shows that although the object that is returned by "theFunc" for both "o1" and "o2" have the same "txt" property with the same value, these objects themselves are different which means the string "hello there" is stored in memory twice, once for each object.

In fact each time "addFunc" is called to add the method "theFunc" to an object, the string "Hello there" is duplicated in memory.

 

So what can we do?

Unless you are specifically interested in the features of nested function mentionned above, the best thing to do is to create all your functions at the same level and only use function references inside a function instead of using nested functions.

For example, if we rewrite the code above, it could be like this:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
theFunc = function() {
  return aVariable;
}
addFunc = function(obj) {
  var aVariable = new Object();
  aVariable.txt = "Hello there";

  obj.theFunc = theFunc;
}

o1 = new Object();
o2 = new Object();

addFunc(o1);
addFunc(o2);

trace(o1.theFunc().txt); // undefined
trace(o2.theFunc().txt); // undefined

trace(o1.theFunc == o2.theFunc) ; // true
Output:

undefined
undefined
true

As you can see above now the trace statements at line 17 and 18 output undefined. That's because, when "theFunc" is called on "o1" and "o2", and Flash cannot find the reference "aVariable" in the scope chain, which means the activation object of "addFunc" had not been added to the scope chain of the methods and the local variable "aVariable" had been deleted as expected.

Furthermore, line 20 now outputs "true" which means the function object for both "o1" and "o2"is the same and takes only one slot in memory.

On top memory space consideration, this approach will also make your code runs faster. Basically, it does take time for a new function to be created in memory: time for memory allocation, time for data transfer, etc. So when you use nested function, each time you call the outer function, you are taking up a few CPU cycles to build the inner function. Creating the functions up-front and doing only a simple assignment in the outer function will make it perform a lot faster.

 

Conclusion

This concludes the topic of scope chain and memory waste. To summarize, here are the keypoints we have discussed:

  1. The scope chain is a sequence of objects that Flash will inspect when it looks for a variable.

  2. When a function is created, the current scope chain is attached to it.

  3. Each time a function is executed, a new object, called the activation object, is created to hold all the local variables and this object is placed on top of the function's attached scope chain.

  4. When nesting functions, the activation object of the outer function is placed in the scope chain of the inner function.

  5. If the inner function gets attached to a persistent object as a method, or is returned from the outer function, then the inner function becomes persistent and with it the activation object of the outer function. This leads to memory waste.

  6. To avoid memory waste, a simple solution is to not use nested function but create function externaly instead and attach references only.

 

In fact, this is all quite simple. The problem is that it is not obvious at all. Many a time, you may unknowingly waste resources.

Before you freak out and think you should go and modify all the files where you used nested functions, I'd like to say that in most cases, this whole thing probably doesn't matter much. In small project, it's not like you are sucking up all the resources of the machine and if your project works fine as it is, just don't worry about it. What I mention here probably applies for huge project and I think it's just nice to know what Flash is doing in the back of your scripts.

I would think that the biggest memory waste cases happen when you deal with large text content and nested functions. Strings can quickly reach up to a few dozens or even hundreds characters so if you are using a long string as a local variable and it becomes persistent, then you may have a real problem, if it's just a few integers or references, it shouldn't be too bad.

Don't make me say what I haven't, as far as I am concerned, the cleaner the file, in term of file size, memory usage and efficiency, the better. So just keep this thing at the back of your mind when you code in ActionScript :).

Thanks for reading up to here. I hope you found this useful. If you spot anything incorrect (code, facts, terminology, grammar, spelling, etc.) or you think different examples may be better suited to explain what is happening or just would like to share new things with me, please feel free to email me. I'd be more than happy to get feedback.

Also, before we go, I thought this topic would raise some extra questions so I added a section called "extras" below.

And for a last note: a feature is always a ground for many practices. Nesting functions may lead to memory waste but it could be an aceptable price to pay for particular applications. For instance, private ans static properties can be implemented using this scope chain feature; more about that in another article.

 

References

Most of what is here comes straight from various threads on the FlashCoder's list. The thread I think is the most interesting is this one:

http://chattyfig.figleaf.com/ezmlm/ezmlm-cgi?1:sss:56601:200212:blejmgjoemfcdojimbmn#b

 

Extras - Questions/Answers

Just how deep can the scope chain be?

Well, I have no idea :) . I tried nesting function up to 5 levels below and all the activation objects became persistent.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
a1 = 5;
addFunc = function(obj) {
  var a2 = 6;
  var func = function(obj) {
    var a3 = 7;
    var func = function(obj) {
      var a4 = 8;
      var func = function(obj) {
        var a5 = 9;
        obj.retrieve = function(refName) {
          var a6 = 10;
          trace(eval(refName));
        }
      }
      func(obj);
    }
    func(obj);
  }
  func(obj);
}
o = {};
addFunc(o);
o.retrieve("a6"); // 10
o.retrieve("a5"); // 9
o.retrieve("a4"); // 8
o.retrieve("a3"); // 7
o.retrieve("a2"); // 6
o.retrieve("a1"); // 5
Output:

10
9
8
7
6
5

Since we can retrieve all the variables, it means the activation object for each function was kept in the scope chain of the most inner function.

So really, I don't know how deep the scope chain can be but it probably can be pretty deep. In any case, the code above already has 5 levels of nested functions and if your code looks like that in a real project, I think you should serioulsy reconsider your coding strategy :) !

 

Scope chain and prototype chain

We mentionned earlier than when Flash looks for a variable, it searches through the scope chain. That is true and on top of it, there is another mechanism that Flash uses when it looks for a variable. That mechanism is the inheritance chain, also known as the prototype chain. Since this article is not a discussion on OOP, I won't go deep and just refer you to Robin Debreuil's online book which is the best resource for understanding how things work in OOP in Flash.

In a nutshell, every object in Flash is an instance of a class and a class can be inherited from yet another class an so on. Each class may have its own set of properties and method and when you call a method on an object, if the method is not found in the object itself, Flash will look-up the inheritance chain to see whether it can find the requested method higher up.

Although the scope chain is handled internaly, the prototype chain IS available to developers and the link between object in the prototype chain exists through the reference "__proto__". When a search in the scope chain is involved, the prototype chain look-up is done for each object in the scope chain.

The following code demonstrate it:

Code:
1
2
3
4
5
6
7
8
9
10
11
a = 5;
addFunc = function(obj) {
  var __proto__ = new Object();
  __proto__.a = 6;
  obj.meth = function() {
    trace(a);
  }
}
o = {};
addFunc(o);
o.meth(); // 6
Output:

6

The value "6" is not assigned to "a" as a local variable in the function "addFunc", yet it was picked up when Flash looked for "a". So step by step, what did Flash do, when "meth" executed?

Well, firstly, Flash looked for "a" in the activation object of "meth" but could not find it there, next, Flash checked whether the activation object of "meth" had a "__proto__" reference. Since there is none (or none we can see), it moved forward to inspect the next object in the scope chain which is the activation object of "addFunc". "a" was not found there. Flash then looked for a "__proto__" reference in that object and found one. Flash looked for "a" in the object pointed at by the "__proto__" reference and found it there! And finaly Flash printed out that value.

 

How are variables assigned without explicit scopes?

I don't know whether I should have shown that at the very beginning or not. Again, this is a behavior that is not complicated but not obvious at all. When you do an assignment, e.g "myVar = 5;", each of the object in the scope chain are inspected to retrieve a reference "myVar" EXCEPT _global. If for one of these object (call it "o"), a "myVar" reference is found the assignment is done on "o".

If no "myVar" reference is found, then a new reference "myVar" will be created in the lowest object in the scope chain. That's the object just above _global.

For assignment, apparently, the prototype chain of the objects in the scope chain is not inspected. So even though a reference "myVar" may exist in the prototype chain of "o", the assignment will not be done on "o" unless it is the last object in the scope chain above _global.

To create or set the value of a property of the _global object, _global must be named explicitely.

Below are a few test scripts to illustrate this:

Code:
1
2
3
4
5
6
7
8
9
10
11
o1 = {};
o2 = {};
with (o1) {
  with (o2) {
    trace("The first 'a' reference found is: " + a);
    a = 5;
  }
}
trace(o1.a); // undefined
trace(o2.a); // undefined
trace(a); // 5
Output:

The first 'a' reference found is:
undefined
undefined
5

 

Code:
1
2
3
4
5
6
7
8
9
10
11
o1 = {a:4};
o2 = {};
with (o1) {
  with (o2) {
    trace("The first 'a' reference found is: " + a);
    a = 5;
  }
}
trace(o1.a); // 5
trace(o2.a); // undefined
trace(a); // undefined
Output:

The first 'a' reference found is: 4
5
undefined
undefined

 

Code:
1
2
3
4
5
6
7
8
9
10
11
12
o1 = {};
o1.__proto__ = {a:4}
o2 = {};
with (o1) {
  with (o2) {
    trace("The first 'a' reference found is: " + a);
    a = 5;
  }
}
trace(o1.a); // 4
trace(o2.a); // undefined
trace(a); // 5
Output:

The first 'a' reference found is: 4
4
undefined
5

 

With and the scope chain

I love "with" and I hate "with". Because it interacts with the scope chain, it has a lot of similarities with nested functions. For instance what we discussed above about assignment without explicit scopes is true for both nested function and "with". However, there are some differences. The major one I want to show here is linked to function creation. I mentionned way above in the article that the current scope chain is attached to a function when it is created. As we have seen, that is what causes activation object persistence. If the current scope chain is attached to a function upon creation, that means we can use "with" to add any object in the function's scope chain. However, it doesn't work. Look at this code:

Code:
1
2
3
4
5
6
7
8
9
o1 = {a:5};
o2 = {};
with (o1) {
  trace(a); // 5
  o2.aMethod = function() {
    trace(a);
  };
}
o2.aMethod(); // undefined
Output:

5
undefined

In the code above, we used "with" to place the object o1 on top of the scope chain. Line 4 shows that we can retrieve "a" properly. Given the current scope chain, we then create a new function (line 5) and assign it to o2 as a new method. Later when we call "o2.aMethod();", it returns undefined. It means "a" was not found in the scope chain and that mean the object o1 was not added to the function's scope chain.

Object placed manually in the scope chain with "with" are NOT considered part of the current scope chain for function creation.

 

What about the "this" reference?

As far as I could test, the reference "this" is not affected by the scope chain, no matter the depth of it. "this" always refers to the object from which the function is called.

Stated differently, while the scope chain for a function never changes, the meaning of "this" inside the function body changes, depending on which object the function gets called from. If we go back to one of the very first example:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = 5;
test = function() {
  trace(this.a);
};

obj = new Object();
obj.a = 6;
obj.meth = test;

obj2 = new Object();
obj2.a = 7;
obj2.meth = test;

obj.meth(); // 6
obj2.meth(); // 7
Output:

6
7

Saying of "this" that it "always refers to the object from which the function is called" leads to a very interesting property. We mentionned earlier that local variables are just properties of the activation object. If, using var, we attach a function to the activation object and run the function from inside the activation object, the reference "this" in that function will refer to the activation object itself! With this fact, it is possible to extract a reference to a particular activation object and store it somewhere (if you can find any use for that) or use the array operator to retrieve or set dynamically property inside the activation object (again, if you can find any use for that).

Code:
1
2
3
4
5
6
7
8
9
a = 4;
test = function() {
  var a = 5;
  var getRef = function() {
    trace(this.a);
  }
  getRef(); // 5
}
test();
Output:

5

 

 

Array operator and eval

People are always wondering whether the actionscript eval is deprecated or not and what is the difference between eval and the array operator. A quote from Ralf Bokelberg provides the answer to these questions in this post, quote is below:

<quote>
Do you know the mantra of eval:

eval is not deprecated
eval is not useless
eval is just fine to use

how else would you access objects given by a targetstring

obj = {subobj: {prop: 666}}
path = "obj.subobj.prop";
trace(this[path]); //undefined
trace(eval(path)); //666

bokel
</quote>

So, yes, one of the major difference is that eval can resolve path while the array operator can only retrieve an immediate property of an object.

With what we have discussed above, another way to state the difference between array operator and eval it to show it is in relation to the scope chain and the prototype chain. Assuming we are using eval or the array operator to retrieve a single variable (no path), then we could say: the array operator is used to retrieve a variable (property) by inspecting the prototype chain of a given object; eval is used to retrieve a variable by inspecting the current scope chain. And, as we have mentionned above, inspecting the scope chain involves inspecting the prototype chain for each object in the scope chain.

 

Did you understand it all?

All right, so this is the very end. With all we have discussed above and wihtout using Flash MX, can you tell what will be the ouput from the following code?

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
getMethod = function() {
  var setProto = function() {
    this.__proto__ = o1;
  };
  setProto();

  return function() {
    trace(a);
  }
}

_global.a = 4;
o1 = {a:5};
o2 = {a:6};
a = 7;

o2.theMethod = getMethod();
o2.theMethod();
  1. 4
  2. 5
  3. 6
  4. 7

Last modified on:

File size: