This is an archived tutorial from the kirupa.com legacy collection. It covers software that may no longer be available, but it is kept online because the ideas still hold up.
A prototype is class specific shared value. Each class has its own prototype object which is used for storing shared prototype values. These values can be variable values such as numbers or strings or, and more commonly the case, as class method functions. Everything contained within this prototype object is then accessible to each instance of that class despite the fact that it is not part of that actual instance itself.
The prototype object for any class resides in the
constructor function for that class. And that’s all the
prototype object is, just a simple object that sits there in
the constructor function waiting to have shared definitions
assigned to it. It’s created automatically by Flash when the
constructor is defined so you won't have to worry about
making it manually. In fact, Flash automatically creates a
prototype object for any function you define assuming its
possible use as a constructor function.
doesNothingFunction = function(){};
trace(doesNothingFunction.prototype); // traces [object Object]
The quickest way to get started with prototypes is using them on Flash's core objects (classes) such as the Array object of the MovieClip object. When you define values or functions in the Array.prototype object, they become available to all Array instances. This can be very helpful if you are using something like a common method for a lot or all of your Array in a Flash movie. Defining it once in the Array.prototype means all arrays instantly have access to use that function as though it were assigned directly to it since everything in the prototype object of Array is shared among all array instances.
As an example we can use the switchValues function from
before. This function switched the values of the first two
elements of an array. As an array prototype, the function
acts as a method to all arrays and wouldn’t have to be
defined individually for each array that would need to use
it.
Array.prototype.switchValues = function(){
var temp = this[0];
this[0] = this[1];
this[1] = temp;
};
letters = ["A", "B"];
numbers = [1,2];
letters.switchValues();
trace(letters); // traces "B", "A"
numbers.switchValues();
trace(numbers); // traces 2, 1
Having been set only once in the Array.prototype, all arrays are then able to use the function as an array method – as though it was defined in directly in each.
Here is an example of a MovieClip prototype that will, when called, cause any movieclip to jump right 100 pixels:
MovieClip.prototype.jumpRight = function(){
this._x += 100;
};
// used it on clipA in the current timeline
clipA.jumpRight(); // clipA moves 100 px to the right
In fact, every method you already know as array or movieclip methods are those that already exist in the Array and MovieClip prototype objects. This applies to all methods of predefined Flash objects. Each one of those methods for each Actionscript object is defined in that class’s prototype object. The valueOf and toString methods, for example, are prototype methods.
Just as you would add a prototype to a pre-existing Flash
classes, you can also add them to your own. Take the House
class for example. Before, we added a method directly in the
House class constructor. In doing this, all House instances
created would receive a copy of that function. Keeping that
method in the House prototype object instead of assigning a
copy within the constructor means that all House instances
will still have access to the method, but it will only need
to be physically defined in one place. Then it can be shared
in an optimized non-redundant manner.
// class definition
House = function(siding){
this.floors = 4;
if (siding != undefined){
this.siding = siding;
}else{
this.siding = "Red";
}
};
// define a method in the House function's prototype object
// which is to be shared among all House instances
House.prototype.outputSiding = function(){
trace(this.siding);
};
// create an instance of the House class
myHouse = new House();
myHouse.outputSiding(); // traces "Red"
// create an blue instance of the House class
myHouse = new House("Blue");
myHouse.outputSiding(); // traces "Blue"
With just about every class you make, you'll be wanting to assign property values in the constructor copying them to instances, as these are likely to change or be different for each instance, and have methods shared in the prototype object of the constructor as they are not unique to each instance.
Though the prototype object, though commonly used for methods, is not solely for methods alone. Variable properties of other more simple values can be defined there as well. These can be variables that you wish consistent for all object instances or also values to serve as defaults for otherwise unique properties in objects.
An example of such a property as a default would be Button and MovieClip objects’ useHandCursor property. By default, each Button and Movieclip has a useHandCursor value of true. This means when button actions such as onPress are assigned to either a button or movieclip, the default arrow cursor will change to a hand icon when the cursor is over the button or movieclip. This true value is stored in the button and movieclip prototypes and therefore is set for each button and movieclip created.
When you want individual instances to have their own useHandCursor value, you can just define a new useHandCursor value for that instance itself. Properties defined directly to object instances will have precedence over prototype values. With this, realize that assigning a value to a property which may not exist in an object instance but does exist in the instance’s class prototype won’t mean that you are assigning the value to that prototype variable. You would actually then be creating a new variable specific to the object that will then have precedence over the prototype’s variable value when referenced for that object. So a value referenced and a value assigned may not always be the same variable. If you want a prototype value to change, you will need to do so directly from the prototype object itself, otherwise you would be defining a value in an instance.
The following example will assume a movieclip called
clipA in the timeline.
// trace default value for movieclips’ useHandCursor
trace(MovieClip.prototype.useHandCursor); // traces true
trace(clipA.useHandCursor); // traces true
// change the prototype and see it reflected in clipA
MovieClip.prototype.useHandCursor = false;
trace(MovieClip.prototype.useHandCursor); // traces false
trace(clipA.useHandCursor); // traces false
// reset the prototype
MovieClip.prototype.useHandCursor = true;
trace(clipA.useHandCursor); // traces true
// now set a unique useHandCursor for clipA that overrides the prototype
clipA.useHandCursor = false;
trace(clipA.useHandCursor); // traces false
trace(MovieClip.prototype.useHandCursor); // traces true
When incorporating such defaults into your own classes,
be sure not to set anything for the value you wish to
default or it will override what is set in the prototype.
For example, using the house class, in defaulting the
siding, siding should only be set at all in the house
constructor if the siding is passed. When not, the prototype
value will be used.
// class definition
House = function(siding){
this.floors = 4;
if (siding != undefined){
this.siding = siding;
}
};
// default siding property value
House.prototype.siding = "Red";
// outputSiding method
House.prototype.outputSiding = function(){
trace(this.siding);
};
// create an instance of the House class
myHouse = new House();
myHouse.outputSiding(); // traces "Red"
Since no siding was passed in creating myHouse, the if statement skipped the assignment of siding altogether in the constructor. As such, the siding traced in the outputSiding method is the siding referenced from the house prototype object.
For an online collection of prototype functions, see http://proto.layer51.com.
Often you may find yourself writing methods whose sole purpose is to alter a particular single property or aspect of an instance. An existing example of this can be seen in the MX scrollbar component with setEnabled. You can set the enabled aspect of the scrollbar by passing in either true or false to that setEnabled method. There's also a method to check that value, getEnabled. Now if you think about regular buttons, they too have an enabled aspect. For regular buttons, though, enabled is a property handled by direct assignment and not through methods such as setEnabled and getEnabled for a scrollbar. If they are doing the same thing, one would think the scrollbar too could have just a property instead of using methods, right? Well, the problem is that a scrollbar is made up of many buttons and elements that require extra effort to actually "disable" the scrollbar, therefore it must use a method to handle all the necessary operations needed to turn the scrollbar from enabled to disabled. Though it may seem like a minor infection of enabled standards, there is a cure. That cure lies in addProperty.
The addProperty method is a function allowing you to create a property in an object (which could be a prototype object) that uses two functions, a get and set, to handle its definition. The get function is used when the property is checked and the set function when it's assigned a value. This allows the conversion of two functions into one property.
scrollbar.setEnabled(true);
trace(scrollbar.getEnabled()); // traces true
scrollbar.enabled = true;
trace(scrollbar.enabled); // traces true
Get and set methods are accessor methods. They provide a method interface to a property or aspect of an object. Using addProperty, you have the ability to make a property that behaves based on defined accessor methods. This allows your basic property to behave in a very strict manner. For instance, you could set up a property that, though it may have been assigned one value, returns another – all based on the definition of the get and set functions. A more practical use would be to assure that the values assigned to the "property" are valid ones. As an example, lets say we wanted a selected track for a certain album object. This album object would only have a certain number of songs, so we need to make sure a requested track is valid within that listing.
Album = function(title, artist, tracksArray){
this.title = title;
this.artist = artist;
this.tracksArray = tracksArray;
this.$selected = 1; // used to hold the "real" selected track number
};
getSelected = function(){
return this.$selected;
};
setSelected = function(trackNum){
if (trackNum < 1){
trackNum = 1;
}else if (trackNum > this.tracksArray.length){
trackNum = this.tracksArray.length;
}
this.$selected = trackNum;
};
Album.prototype.addProperty("selected", getSelected, setSelected);
nowPlaying = new Album("OOP - Yeah You Know Me", "The Flashers", ["A", "B", "C"]);
nowPlaying.selected = 2;
trace(nowPlaying.selected); // traces 2
nowPlaying.selected = 20;
trace(nowPlaying.selected); // traces 3
You can see here that the added getter/setter property was added to the prototype of the Album class. That will still work fine for any instance of Album – just as it were any other method or property defined there. It will be accessible to all instances just the same. Notice too that the getSelected and setSelected are not methods defined in the Album prototype. This is because addProperty does not use current methods of an object, but rather, as its name suggests, adds them – actually copying them. So whatever methods are used in the addProperty call will be added to the object though only accessible through the use of that property. If you want, you can delete the original functions if you don't want them lingering around. You could use also prototype methods if you wanted to, though they will be added into the object a second time – once as they exists as prototypes, and again for use in the new property. This of course may be desired if you want both method and property access to the aspect. In defining those, just be sure to reference the function fully.
Album.prototype.addProperty("property", Album.prototype.getSelected, Album.prototype.setSelected);
Also, if you noticed, in the Album example there was a need to keep a "real" hidden property to represent the actual selected property. The selected property as it was defined by addProperty was just a method for changing the real thing. Here, a $ was used to separate the getter/setter property from the real one. The difference in variable names didn't have to be a $. It could have been another variable all together – just something to keep hold the desired end value. The use of $ serves as an easily seen indicator that the property is considered "hidden" as you don't often see variables using the dollar sign in Flash. Feel free to use what you are most comfortable with when dealing with these kinds of situations.
For one more example, we can make something similar to that of the enabled scrollbar. This, instead, will be a .collapsed property for a simple menu. When not collapsed, the menu is its normal size and visible. When collapsed, it's compacted into just an icon.
Example:
[ hidden property set to true when mouse is down ]
// the menucontrol class controls a menu movieclip
// in the main timeline.
MenuControl = function(menu, icon_name){
this.menu = menu;
this.icon_name = icon_name;
};
getCollapsed = function(){
// if any non-icon clip has
// a false _visible property then
// collapsed is true (!false = true)
for (var item in this.menu){
if (item != this.icon_name){
return !this.menu[item]._visible;
}
}
};
setCollapsed = function(visible){
// cycle through all clips in the menu
// clip. If it has a _visible property
// set its value to be the value set by
// the assignment of .collapsed
for (var item in this.menu){
if (item != this.icon_name){
if (this.menu[item]._visible != undefined){
this.menu[item]._visible = !visible;
}
}
}
};
// create the property
MenuControl.prototype.addProperty("collapsed", getCollapsed, setCollapsed);
// delete the functions used as there is no longer a need for them
delete getCollapsed;
delete setCollapsed;
// menu is a movieclip on the main timeline
// icon is one of many movieclips inside menu
// when collapsed, all other movieclips (not icon)
// are set to be invisible. When collapsed is false
// then all movieclips are set to be visible
menu = new MenuControl(menu_mc, "icon");
// add some basic interactivity using buttons
// to deomonstrate collapsing of the menu
false_btn.onRelease = function(){
menu.collapsed = false;
trace("hidden: "+ menu.collapsed);
};
true_btn.onRelease = function(){
menu.collapsed = true;
trace("hidden: "+ menu.collapsed);
};
The idea of encapsulation is that objects are “self contained” and the processes and properties of their inner workings should not be exposed in their use. This lends to the idea of objects being little black boxes.
The little black box is the solve-all solution to your problems. No one really knows how one works, it just does. In goes your problem, out comes the solution. There has been much talk recently about the little black box for home entertainment systems – the all-in-one VCR/DVD/DVR/cable/stereo/video game system/whatever. As a consumer, you won’t need to know how it works internally, just that it does work and it does everything for you that you would ever need in a home entertainment system. One of the ideas of OOP is to have little black box objects that do what they need to do without the developer having to worry about how it does it.
Also, as a developer, you would want to tamper-proof your object definitions so other people (or even yourself for that matter) can’t get into the inner workings of such objects and throw a wrench in the gears thus possibly causing problems in functionality. You want objects to be self- contained, portable entities that function as they should when they should without worry of failure (which is important in portability – the ability to move your object classes from one project to the next).
Some programming languages offer means to protect interior class workings giving you the ability to define public and private properties and methods. Public values are those readily accessible from an instance of a class – those which are supposed to be used to operate the instance. This would be the remote control to your black box home entertainment center. The remote represents you, the viewer’s, control. It doesn’t let you mess with resister 5A inside the box, but it does provide you with an interface for using the box. These equate often to method functions. Private values are those which are not accessible accept from within the inner workings of that object. This includes that 5A resister and anything else in the box that makes it work but the viewer isn’t supposed to know or mess with.
Sadly, Actionscript 1.0 does not support public or privately defined properties or methods. In Actionscript everything is public and openly accessible. It can be seen as a good thing as it does give you more control. However, in terms of being an OOP language, it’s a hindrance. They don’t call it Flash Actionscript for nothing; it’s really exposed. Thank you, thank you. I’m here all night.
In the addProperty example concerning a "collapsible" menu, a for..in loop was used to cycle through the elements of a movieclip on the screen. The process of going through all the elements of an object, be it movieclip or otherwise, is called enumeration.
All properties and methods defined for an object are enumerable through the use of a for..in loop in Flash. This includes properties created with addProperty as well as prototype properties and methods. Flash properties, even inherited ones, are all public and available like that, even in for loops and enumeration. Such a for..in loop, however, does NOT include hidden properties like constructor, __proto__ or __constructor__ (the latter two are discussed later). With your run of the mill prototype properties, though, you may find a for..in loop to be giving you more than you bargained for. See the following example.
Notice how not only were the names defined for the object as properties given, but also the reportUsers method itself. The for..in caught that as well, recognizing it as a property of the UsersGroup instance. Of course with every problem there is a solution (atleast that’s something good to keep telling yourself). The solution to this problem is ASSetPropFlags.
ASSetPropFlags is a function in Actionscript that allows you to define for an object how its properties are handled; whether they can be changed, deleted or enumerated. To read more about ASSetPropFlags, see flashcoders explanation. Using it on a prototype object, or really any property you desire, will allow you to control how your object instances are enumerated.
If using for..in loops with your objects, this can be an important function to keep handy. Unfortunately, it’s undocumented by Macromedia so don’t be surprised if finding information on ASSetPropFlags seems harder than it should be. On that note, there’s yet another hidden method in the same vein of ASSetPropFlags, isPropertyEnumerable. The isPropertyEnumerable will tell you whether or not a property will be found in a for..in loop. For example, if used on reportUsers above, false would be returned. On Daisy, however, true would be returned.
trace(groupA.isPropertyEnumerable("reportUsers")); // traces false (hidden with ASSetPropFlags)
trace(groupA.isPropertyEnumerable("Daisy")); // traces true
The idea behind sharing properties and methods like this is consistent throughout other concepts in OOP. It does not exist solely in or end with prototypes alone. Inheritance among classes is also based upon such sharing.
That wraps up this tutorial. A huge thank you to all of you who buy kirupa's books, became a paid subscriber, watch the videos, and/or interact on the forums. Your support is what keeps writing like this online! 😇
This tutorial was written by senocular, also known as Trevor McCauley. He has been one of this community's most generous teachers since the early Flash days, and he is still around: find him on senocular.com and on the forums.
:: Copyright KIRUPA 2026 //--