| Ryerson Home > Ryerson Flash Communication Development Server > Are Function Literals More Expensive? |
In the early days of JavaScript, methods were defined using function declarations. However, in ActionScript function literals are much more common. I'm not sure if this is always a good thing. This page explains why and asks some follow on questions that I don't know the answer to.
There are two ways to define a function in ActionScript:
function myFunction(){
trace("Welcome to my function.");
}
myFunction = function(){
trace("Welcome to my function.");
}
Both of these functions can be executed this way:
myFunction();
And, both do the same thing.
Similarly methods can be defined in one of two ways:
function myFunction(){
trace("Welcome to my method.");
}
function MyClass(){
}
MyClass.prototype.saySomething = myFunction;
function MyClass(){
}
MyClass.prototype.saySomething = function(){
trace("Welcome to my method.");
}
Both provide a way to create a method for an object that can be used this way:
x = new MyClass(); x.saySomething();
There doesn't seem to be much difference between the two method definitions other than:
myFunction and the other
stored in MyClass.prototype.saySomething - this is useful in
some circumstances and a little wasteful in othersfunction Class(){
}
Class.prototype.saySomething = myFunction;
function myfunction(){
trace("Welcome to my method.");
}
However, consider the following snippet of server-side ActionScript where a function literal is used inside another function literal:
application.onConnect = function(client) {
application.acceptConnection(client);
client.loginPlayer = function(player){
if(application.playerList[player]){
return "Someone else is using the name " + player + ".";
}else{
application.playerList[player] = true;
client.name = player;
return "Welcome " + player + ".";
}
};
};
In particular look at this line of code from the inner function literal:
client.name = player;
What is going on here? Shouldn't this be:
this.name = player;
After all, client is the object and loginPlayer is
a method of the client object. Normal practice is to use this
to refer to the object. Of course this looks harmless enough. But in fact it
is wasteful. ActionScript functions are executed in the scope in which they
are defined. In this case the function literal assigned to loginPlayer
is defined within the onConnect method. Since the client
local variable exists where the loginPlayer method is defined,
ActionScript puts away a copy of the client variable for use by
each loginPlayer method every time it is assigned! Since we already
can refer to the object using this the extra copy of a reference
to each individual client object is not necessary.
One way to guarantee that no local variables are stored along with each copy
of a function reference is to use the prototype object and the keyword this.
This can be done using a function declaration:
function Client_loginPlayer(player){
if(application.playerList[player]){
return "Someone else is using the name " + player + ".";
}else{
application.playerList[player] = true;
this.name = player;
return "Welcome " + player + ".";
}
Client.prototype.loginPlayer = Client_loginPlayer;
application.onConnect = function(client) {
application.acceptConnection(client);
};
or it can be done with a function literal:
Client.prototype.loginPlayer = function(player){
if(application.playerList[player]){
return "Someone else is using the name " + player + ".";
}else{
application.playerList[player] = true;
this.name = player;
return "Welcome " + player + ".";
}
application.onConnect = function(client) {
application.acceptConnection(client);
};
Note that in both cases the function is not defined inside the onConnect method
of the application object. Therefore there is no possibility that any local
variables from the application.onConnect method are stored along
with each reference to the function.
However there are a number of cases where the prototype object cannot be used.
For example different client objects may have to have different methods depending
on the role an individual user is assigned. Another example is shared objects.
In some cases the onSync method of one shared object may have to
be different from the onSync method of another one. In these cases
using function literals may be less storage efficient than using function declarations.
How much less efficient I do not know. Here's another snippet to think about.
application.onConnect = function(client, userTicket) {
application.acceptConnection(client);
client.loginPlayer = function(player){
if(application.playerList[player]){
return "Someone else is using the name " + player + ".";
}else{
application.playerList[player] = true;
client.name = player;
return "Welcome " + player + ".";
}
};
};
This example raises some questions:
userTicket and its value stored with each instance
of the loginPlayer function even though it isn't used in the
function or is the interpreter smart enough not to bother?client.name = player is changed to this.name
= player is this the same from a storage perspective as using a function
declaration or is some sort of closure object kept around making nested function
literals more expensive regardless of how they are coded?In any event using a function declaration outside of the acceptConnection
method seems more efficient than the previous listing because no closure object
is created to store the value in client at the time the function
loginPlayer is assigned to each client object:
function Client_loginPlayer(player){
if(application.playerList[player]){
return "Someone else is using the name " + player + ".";
}else{
application.playerList[player] = true;
this.name = player;
return "Welcome " + player + ".";
}
}
application.onConnect = function(client, userTicket) {
application.acceptConnection(client);
client.loginPlayer = Client_loginPlayer;
};
So, the advantage to function declarations appears to be that they can be made outside of a method declaration. Then the function name can be used as a reference to the function in a statement like this:
client.loginPlayer = Client_loginPlayer;
You can't do that with function literals.
There is nothing intrinsically different about function declarations and function literals when it comes to scoping rules. When you have nested functions a closure object is created to remember variable values. Consider the following script to see this:
y = 100;
function outerFunction(x){
trace("y is " + y);
function innerFunction(){
trace("x: " + x );
}
return innerFunction;
}
f1 = outerFunction(1);
y = 101
f2 = outerFunction(2);
x = 100;
f1();
f2();
trace(outerFunction);
trace(innerFunction);
/* output for this script:
y is 100
y is 101
x: 1
x: 2
[type Function]
undefined
*/
Finally in some circumstances a function declaration is useful because a reference to the function is always available using the function name. For example you may wish to temporarily stop all further handling of events by doing something like this:
nc.onStatus = null;
Later, you may want to restart event handling this way:
nc.onStatus = NetConnection_onStatus;
In fact it may be useful to change the way a handler behaves by assigning it different function references:
nc.onStatus = NetConnection_onStatusClosed;
and at another time:
nc.onStatus = NetConnection_onStatusOpen;
Function literals are often used inside other function literals or declarations and sometimes make use of the local variables available within the outer function. This is a useful feature of ActionScript that comes along with the price of increased memory usage. In some cases this is unnecessary and can be avoided by using function declarations instead of using function literals. Even where function literals are used, avoiding using the local variables of the outer function may avoid any memory penalty.
Colin Moock's tech note: http://www.moock.org/asdg/technotes/functionLiteralVsFunctionStatement/index.html
David Flanagan's book: JavaScript the Definitive Guide: http://www.oreilly.com/catalog/jscript4/ - in particular the section Lexical scoping and nested functions.
This document was written by Brian Lesser and originally posted: 10/04/2002. It was last updated: