Quantumwave Interactive Inc.
  Interactive media development . Programming . Design . Consulting

Demonstration of object inheritance in Flash 5 ActionScript:


// First, create some utility and display methods by
// extending or overriding some built-in methods:
// -------------------------------------------------------------------------------

// Count the number of properties in an object
Object.prototype.count = function() {
   var n = 0;
   for (var p in this) n++;
   return n;
};

// Display properties and methods in an object
Object.prototype.showProps = function(varName) {
   if ((varName != null) && (varName.length > 0)) varName += " ";
   trace("Object "+ varName +"{");

   for (var p in this) {
      var pVal = this[p];
      var pType = typeof pVal;
      if (pType == "function") {
         trace("\t"+ pType +" "+ p +"()");
      } else {
         trace("\t"+ p +":"+ pVal);
      }
   }
   trace("}");
};

// Override the system Object.toString() method
Object.prototype.toString = function() {
   var lastProp = this.count()-1;
   var s = "";
   var i = 0;

   for (var p in this) {
      var pVal = this[p];
      var pType = typeof pVal;
      if (pType != "function") {
         if (pType == "string") {
            s += p +":\""+ pVal + "\"";
         } else {
            s += p +":"+ pVal;
         }
         if (i < lastProp) s += ", ";
      }
      i++;
   }
   return "{"+ s +"}";
};

// Store the system Array.toString() method
Array.prototype.sysToString = Array.prototype.toString;

// Override the system Array.toString() method
Array.prototype.toString = function() {
   var lastItem = this.length-1;
   var s = "";

   for (var i=0; i<this.length; i++) {
      var iVal = this[i];
      var iType = typeof iVal;
      if (iType != "function") {
         if (iType == "string") {
            s += "\""+ iVal + "\"";
         } else {
            s += iVal;
         }
         if (i < lastItem) s += ", ";
      }
   }
   return "["+ s +"]";
};
// -------------------------------------------------------------------------------

// Create the object hierarchy starting with the Parent
function Parent(a) {
   this.a = a;
}

// Method to display property a
Parent.prototype.showA = function() {
   trace("a:"+ this.a);
};
// -------------------------------------------------------------------------------

// Create the Child object to inherit from the Parent, and pass on a parameter
function Child(a, b) {
   this.b = b;
   this.base = Parent;
   this.base(a);
   delete this.base;
}

// Inherit from the Parent:
// Child.prototype = new Parent;
// The above inheritance method creates duplicated properties in Flash 5
// (see second trace output)
Child.prototype.__proto__ = Parent.prototype;

// Method to display property b
Child.prototype.showB = function() {
   trace("b:"+ this.b);
};
// -------------------------------------------------------------------------------

// Create the Grandchild object to inherit from the Child, and pass on two parameters
function Grandchild(a, b, c) {
   this.c = c;
   this.base = Child;
   this.base(a, b);
   delete this.base;
}

// Inherit from the Child:
// Grandchild.prototype = new Child;
// The above inheritance method creates duplicated properties in Flash 5
// (see second trace output)
Grandchild.prototype.__proto__ = Child.prototype;

// Method to display property c
Grandchild.prototype.showC = function() {
   trace("c:"+ this.c);
};
// -------------------------------------------------------------------------------

// Start testing with some data
var x = {x:60, y:["a","b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true};
var y = [13,"m",{f:"g"},[[x.z.d.d1, x.y],["h",{i:!x.e}]]];
var z = [{fname:"dave", lname:"yang", url:"quantumwave.com"}];

var p = new Parent(x);
var c = new Child(x, y);
var g = new Grandchild(x, y, z);

p.showProps("p");
c.showProps("c");
g.showProps("g");

g.showA();
g.showB();
g.showC();

// Trace output:

Object p {
   function showProps()
   function count()
   function showA()
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
}
Object c {
   function showProps()
   function count()
   function showA()
   function showB()
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
   b:[13, "m", {f:"g"}, [[42, ["a", "b"]], ["h", {i:false}]]]
}
Object g {
   function showProps()
   function count()
   function showA()
   function showB()
   function showC()
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
   b:[13, "m", {f:"g"}, [[42, ["a", "b"]], ["h", {i:false}]]]
   c:[{fname:"Dave", lname:"yang", url:"quantumwave.com"}]
}
a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
b:[13, "m", {f:"g"}, [[42, ["a", "b"]], ["h", {i:false}]]]
c:[{fname:"Dave", lname:"yang", url:"quantumwave.com"}]

// For comparison, here is the trace output using the standard prototype methods:
// (Child.prototype = new Parent) and (Grandchild.prototype = new Child)
// JavaScript, JScript or ScriptEase doesn't display this bug/feature. 8-)

Object p {
   function showProps()
   function count()
   function showA()
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
}
Object c {
   function showProps()
   function count()
   function showA()
   function showB()
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
   b:[13, "m", {f:"g"}, [[42, ["a", "b"]], ["h", {i:false}]]]
}
Object g {
   function showProps()
   function count()
   function showA()
   function showB()
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
   function showC()
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
   b:[13, "m", {f:"g"}, [[42, ["a", "b"]], ["h", {i:false}]]]
   a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
   b:[13, "m", {f:"g"}, [[42, ["a", "b"]], ["h", {i:false}]]]
   c:[{fname:"Dave", lname:"yang", url:"quantumwave.com"}]
}
a:{x:60, y:["a", "b"], z:{c:-11, d:{d1:42, d2:3.1416}}, e:true}
b:[13, "m", {f:"g"}, [[42, ["a", "b"]], ["h", {i:false}]]]
c:[{fname:"Dave", lname:"yang", url:"quantumwave.com"}]
Download source files: Flash5ObjectInheritance.zip or Flash5ObjectInheritance.sit

 
Notes:

Most of the code can be adapted for use in JavaScript or ECMAScript. However, different implementation of "the language" handles objects differently. For example, Internet Explorer/JScript and ECMAScript do not support __proto__; Netscape JavaScript, Microsoft JScript, and Nombas ScriptEase display objects in different orders.

Hopefully with this demonstration, programmers new to object-oriented programming in Flash can start experimenting with object inheritance. Not only can simple objects be extended, MovieClip, XML, String and other objects can also be extended in an object-oriented manner.

Please note that there are many ways to inherit objects in ActionScript; however, here are some of reasons why I do it in the way above:

1) no extra properties are added to each instance.

I've seen others implement inheritance this way:

function Parent(a) {
   this.a = a;
}

function Child(a, b) {
   this.b = b;
   this.sConstructor(a);
}
Child.prototype.sConstructor = Parent.prototype.constructor;
Child.prototype.__proto__ = Parent.prototype;

This works, but an extra property (sConstructor) is created and attached to every instance because it is not removed after the relationship is established.

Also, because Parent.prototype.constructor == Parent, the second line with sConstructor can be written as:

Child.prototype.sConstructor = Parent;

2) no overhead of duplicated methods in each instance:

function Parent(a) {
   this.a = a;
   this.showA = function() {
      trace("a:"+ this.a);
   };
}

This also works, but each instance is carrying its own copy of the method showA(). What this means is you cannot change one prototype method later on to affect all instances (a powerful JavaScript feature):

Parent.prototype.showA = function() {
   trace("Brand new showA():"+ this.a);
};

Instead, each instance's showA() method needs to be changed if needed. On the other hand, if this is what you intend to achieve (static instance methods), this is one way to do it.

3) no object is instantiated during object hierarchy definition; only a reference is made.

I've also come across code which looks like the following. What it does is instantiate a new Parent object, but inheritance is not actually established.

function Child(a, b) {
   this.b = b;
   this.__proto__ = new Parent(a);
};

4) to work around Flash's duplicated properties "feature" (see this article for more info):

ActionScript supports __proto__, so we can use it to establish inheritance to avoid the side effect of duplicated properties (whether it is because of a bug in the "for in" loop or not).

For general JavaScript usage (or more specifically JScript and ECMAScript), the standard method of inheritance should be used for compatibility across browsers.

Miscellaneous notes:

Comparing constructors:

When comparing an object's constructor with a class, do not compare using the constructor property in Flash 5. Because of a bug, the comparison always return true, no matter what you compare it to. For example:

Parent = function() {};
obj = new Parent;

trace(obj.constructor == Parent);   // true
trace(obj.constructor == BobTheBuilder);   // true

Instead use something like this:

trace(obj.__proto__ == Parent.prototype);

 

See also: Virtual MovieClip Class & Different ways to inherit

Last updated on: Aug 26, 2001 - Copyright © 2001-2002 Dave Yang / Quantumwave Interactive Inc.

home | about | news | flash | director | shockwave | download | dave | résumé | contact

Copyright © 1995-2009 Quantumwave Interactive Inc.