Flash dynamic references vars

dynamic references

If you’ve been dealing with actionscript for some time and you still don’t know what this is, pay close attention because sooner or later you’ll need it. You may even have needed it already!

What is dynamic referencing?
A way to call an instance of an object, or its properties dynamically.
This can be a movie clip inside another movie clip, or a movie clip’s property, or a property of an object, …

Why is it needed?
Because you may not know instance name of an object.
If you create an empty movie clip using one or more variables, you don’t know how to refer to the movie clip beforehand:

var i:Number = 345;
this.createEmptyMovieClip("mc" + i, i); // the instance name is "mc"+i

By looking at this example, you can tell that the instance name or identifier of the movie clip is mc345, but you don’t know where that variable i comes from! It be a variable that was incremented each time a user clicks a button.

So, how do you reference that movie clip in your script?
That is the main point of this article!

Just as a side note, this might have not been the best example because createEmptyMovieClip() returns a reference to the dynamically created movie clip:

var i:Number = 345;
var new_mc:MovieClip = this.createEmptyMovieClip("mc" + i, i);
trace(new_mc); // outputs _level0.mc345

There are two ways to reference objects dynamically:

  • using the array operator: []
    I like to call it array notation, in contrast with dot notation, and the dot operator: .
  • using the eval() global function

Returning to the previous example:

var i:Number = 345;
this.createEmptyMovieClip("mc" + i, i);
// using array notation
trace(this["mc" + i]); // outputs _level0.mc345
// using eval()
trace(eval("mc" + i)); // outputs _level0.mc345

This "mc" + i thing may be familiar from other functions/methods such as attachMovie() and duplicateMovieClip().
I’d say that the duplicateMovieClip()example that used to be available in the flash reference is a classic already!
Beware that every piece of code in that example is deprecated, and I mention it just for fun! Don’t use it!
For example, setProperty() should be replaced by array or dot notation (depending on the situation)!
For updated help use the livedocs. Check the newest duplicateMovieClip()example.

As you can see by these two lines:

trace(this["mc" + i]);
trace(eval("mc" + i));

what’s inside both the array operator [] and the eval() is a string, a string that is converted to an identifier!

array operator []

This operator is the preferred way to refer dynamically to instances.
This is the syntax:

some_thing["something_else"]
// or
some_thing[some_var] // where some_var is a variable holding a string
// e.g.: some_var = "something_else";

// or a more realistic example:
this["mc" + i]._x = 20;
// or
outer_mc["middle" + i].inner_mc._alpha = 70;

Notice that there is a dot following the closing square bracket when there is an identifier after it. On the other hand there is just an identifier preceding the opening bracket and this identifier has to be always present!

["mc" + i]._x = 20; // is incorrect

In case two pairs of square brackets are used, there is no dot in between them:

this["inner_mc"]["outer_mc"]._x = 20;

For a better understanding of this operator, observe that the following lines of code are all equivalent:

this.createEmptyMovieClip("outer_mc", 1); // create a mc called outer_mc
outer_mc.createEmptyMovieClip("inner_mc", 1); // create a mc called inner_mc inside outer_mc
// 3 different ways of targeting inner_mc
trace(outer_mc.inner_mc);
trace(outer_mc["inner_mc"]);
trace(this["outer_mc"]["inner_mc"]);
// all output _level0.outer_mc.inner_mc

Although the last two lines of code are obviously more complex than the previous one, all three do exactly the same. You’ll never use such code, but look at it as a good example!
In a real situation you could have something like:

this.createEmptyMovieClip("outer_mc", 1);
outer_mc.createEmptyMovieClip("inner_mc", 1);
var1 = "outer_mc";
var2 = "inner_mc";
trace(this[var1][var2]); // _level0.outer_mc.inner_mc

Unlike eval(), the array operator can be used to create variables:

this["fname"] = "nuno";
trace(fname); // nuno
eval("lname") = "mira"; // is not permitted

eval()

If you understand the array operator, you won’t have trouble understanding this function.
eval() also converts strings to identifiers, but unlike the array operator, you can place the complete target path inside the function:

this.createEmptyMovieClip("my_mc", 1);
// you need to use an identifier preceding the opening bracket
trace(this["my_mc"]);
// you don't need the identifier
trace(eval("my_mc"));
// but if you use it, you need to place it inside the eval()
trace(eval("this.my_mc"));
// this isn't valid
trace(this.eval("my_mc"));
// don't forget that eval() is a function
// this.eval() has a different meaning from this.[]

Another example:

var i = 2;
this.createEmptyMovieClip("outer_mc", 1);
outer_mc.createEmptyMovieClip("middle" + i, 1);
eval("outer_mc.middle" + i).createEmptyMovieClip("inner_mc", 1);
// or
//outer_mc["middle" + i].createEmptyMovieClip("inner_mc", 1);

// there are some different options and combinations:
trace(eval(outer_mc + ".middle" + i + ".inner_mc"));
trace(eval(outer_mc + ".middle" + i).inner_mc);
trace(eval("outer_mc" + ".middle" + i + ".inner_mc"));
trace(eval("outer_mc.middle" + i + ".inner_mc"));

Pay close attention to the dots that can’t me missing!
What you’re writing in the previous example is exactly:

trace(eval("outer_mc.middle2.inner_mc"));

Like said before, you can’t use eval to create variables or change their values. But you still can change object’s properties:

var i = 1;
ball1._alpha = 30;
this["ball" + i ]._alpha = 70;
eval("ball" + i )._alpha = 20;

set()

An alternative to eval() to define variables is set().
I don’t thing there is much use for set(), but I’ll mention it anyway. I’ve wondered whether it is deprecated or not, but although it has a deprecated look, there is nothing in the documentation that confirms my opinion.

set("fname", "nuno"); // notice that the variable name is a string
set("age", 24);
trace("my fname is " + fname + " and I'm " + age);

Thanks to this syntax we can create dynamic variables like we did with [] but couldn’t do with eval():

var n = 1;
set("x" + n, 55);
trace(x1);

loops – the power of dynamic reference

Besides all we’ve already seen till now, most of the power of dynamic reference surfaces when working with loops. In many situations, using loops saves a lot of time. Suppose you want to change the _alpha of the 50 movie clips you have on the stage. Of course you could do that by hand… 50 lines of code… What if you had 500?

for (var i = 1; i <= 50; i++) {
this["mc" + i]._alpha = 50;
}
// 3 lines of code and it's done!

Probably you had placed them on the stage using a loop already:

for (var i = 1; i <= 50; i++) {
this.attachMovie("my_mc", "mc" + i, i);
this["mc" + i]._x = 20 * i; // or // eval("mc" + i)._x = 20 * i;
this["mc" + i]._alpha = 50; // or // eval("mc" + i)._alpha = 50;
}

The same applies to the other loops besides for(): while(), do..while(), for..in().

eval() vs []

In this kind of situations there is always the same question: which one should you use?
Like I said before, [] is the preferred but eval() is valid, so, use whichever you prefer! I can’t think of any situation in which you can’t use array notation, and as we’ve seen eval() isn’t always a solution. Array notation is cleaner most of the times, but there are situations in which I prefer to use eval(): for instance, in the example above, it’s almost the same, but when you have methods:

for (var i = 1; i <= 50; i++) {
this["btn" + i].i = i; // store a variable i inside each button
this["btn" + i].onRelease = function()
{
// you can't use the this keyword
// the movie clips are on the same timeline the buttons are
// not inside the buttons
this["mc" + this.i].play(); // fails
eval("mc" + this.i).play(); // works
};
}

Remember that the this keyword inside a method refers to the object the method is applied to, in this case, the buttons.
In case you do want to use [], there is a little trick: defining a variable that points to the timeline.

var here = this; // define the variable here
for (var i = 1; i <= 50; i++) {
this["btn" + i].i = i;
this["btn" + i].onRelease = function()
{
here["mc" + this.i].play(); // works thanks to the variable here
eval("mc" + this.i).play(); // works
};
}

functions

You can reference functions dynamically as well!

function myFunction()
{
trace("function myFunction called");
}
// these 3 lines do all the same:
myFunction();
eval("myFunction")();
this["myFunction"]();

levels

It’s not just the main movie that can makeuse of dynamic referencing. External movies loaded on different levels via loadMovieNum() or using the MovieClipLoader Class can also be referenced dynamically.

I find the syntax using the array operator a bit strange in this case, but it does work!

var n = 3;
this["_level" + n].something;
// or
eval("_level" + n).something;

not so legal names

In actionscript not all the instance names are valid. There are some basic rules you have to follow.
You can read the technote Naming Strategies for Flash for more details.

For example, while

my name = "nuno";

isn’t allowed and throws an error,

this["my name"] = "nuno";
trace(this["my name"]); // nuno

outputs nuno. Unexpected hum?

If you’re a ColdFusion developer, you may know that this behavior is similar to the structures’.

I don’t thing that taking advantage of this can be considered a good practice, but sometimes it can be the only way. Suppose you’re loading data from an external source and the variables have funny names which you can’t control. Array notation can save your life!

Leave a Reply