The Right Way to Check An Object’s Type
There are lots of ways to check the type of an object in AS3. These include the is
operator, the deprecated instanceof
operator, the constructor
field, and a combination of getQualifiedClassName
and getDefinitionByName
. Which is fastest, cleanest, and most effective? Today’s article puts them all to the test to find out!
Let’s go over the contenders so we know what we’re comparing here. First is the is
operator, which is clearly the most common solution to the problem. Its syntax is simply this:
boolResult = myObject is SomeClass
The is
operator was introduced in Flash 9 with AS3, but the instanceof
keyword from AS2 lingers on. It’s less effective than is
, but we should still test it. Usage is extremely similar to is
:
boolResult = myObject instanceof SomeClass
Next is the constructor field that every Object
has. This can be used as a primitive version of is
or instanceof
when you only need to compare classes directly and don’t care about inheritance. Here’s how you’d do that:
boolResult = myObject.constructor == SomeClass
Lastly, there is the combination of getQualifiedClassName
and getDefinitionByName
. This is by far the most complicated approach and still is only useful for directly comparing classes. Here’s how it works:
boolResult = Class(getDefinitionByName(getQualifiedClassName(myObject))) == SomeClass
Now let’s put these to the test with a little app. We’ll create an empty Parent
class and an empty Child
class that derives from it and run four tests for each approach:
- parent is/instanceof/== Parent
- parent is/instanceof/== Child
- child is/instanceof/== Parent
- child is/instanceof/== Child
//////////// // Parent.as //////////// package { public class Parent {} } /////////// // Child.as /////////// package { public class Child extends Parent {} } /////////////////////// // IsInstanceOfSpeed.as /////////////////////// package { import flash.display.*; import flash.utils.*; import flash.text.*; public class IsInstanceOfSpeed extends Sprite { private var logger:TextField = new TextField(); private function row(...cols): void { logger.appendText(cols.join(",") + "\n"); } public function IsInstanceOfSpeed() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); const REPS:int = 1000000; var i:int; var beforeTime:int; var afterTime:int; var bool:Boolean; var parent:Parent = new Parent(); var child:Child = new Child(); row("Operation", "Time"); ////////////// // CONSTRUCTOR ////////////// beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = parent.constructor == Parent; } afterTime = getTimer(); row("parent.constructor == Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = parent.constructor == Child; } afterTime = getTimer(); row("parent.constructor == Child", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = child.constructor == Parent; } afterTime = getTimer(); row("child.constructor == Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = child.constructor == Child; } afterTime = getTimer(); row("child.constructor == Child", (afterTime-beforeTime)); /////////// // GETCLASS /////////// beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = Class(getDefinitionByName(getQualifiedClassName(parent))) == Parent; } afterTime = getTimer(); row("getClass(parent) == Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = Class(getDefinitionByName(getQualifiedClassName(parent))) == Child; } afterTime = getTimer(); row("getClass(parent) == Child", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = Class(getDefinitionByName(getQualifiedClassName(child))) == Parent; } afterTime = getTimer(); row("getClass(child) == Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = Class(getDefinitionByName(getQualifiedClassName(child))) == Child; } afterTime = getTimer(); row("getClass(child) == Child", (afterTime-beforeTime)); //////////// // PROTOTYPE //////////// ///// // IS ///// beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = parent is Parent; } afterTime = getTimer(); row("parent is Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = parent is Child; } afterTime = getTimer(); row("parent is Child", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = child is Parent; } afterTime = getTimer(); row("child is Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = child is Child; } afterTime = getTimer(); row("child is Child", (afterTime-beforeTime)); ///////////// // INSTANCEOF ///////////// beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = parent instanceof Parent; } afterTime = getTimer(); row("parent instanceof Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = parent instanceof Child; } afterTime = getTimer(); row("parent instanceof Child", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = child instanceof Parent; } afterTime = getTimer(); row("child instanceof Parent", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { bool = child instanceof Child; } afterTime = getTimer(); row("child instanceof Child", (afterTime-beforeTime)); } } }
I tested this app using the following environment:
- Release version of Flash Player 14.0.0.125
- 2.3 Ghz Intel Core i7-3615QM
- Mac OS X 10.9.2
- Google Chrome 35.0.1916.153
- ASC 2.0.0 build 354130 (
-debug=false -verbose-stacktraces=false -inline -optimize=true
)
And got these results:
Operation | Time |
---|---|
parent[“constructor”] == Parent | 88 |
parent[“constructor”] == Child | 84 |
child[“constructor”] == Parent | 89 |
child[“constructor”] == Child | 88 |
getClass(parent) == Parent | 466 |
getClass(parent) == Child | 475 |
getClass(child) == Parent | 446 |
getClass(child) == Child | 447 |
parent is Parent | 4 |
parent is Child | 3 |
child is Parent | 4 |
child is Child | 4 |
parent instanceof Parent | 26 |
parent instanceof Child | 29 |
child instanceof Parent | 28 |
child instanceof Child | 32 |
The first observation to make is that the object and class type parameters to each test made no difference at all. That means it’s just as fast (or slow) to check if an object is a Child
or Parent
regardless of which it actually is.
The differences lie in the method used to check. By far the worst is the getClass
approach that combines getQualifiedClassName
and getDefinitionByName
. It’s taking 5x longer than the next-worse solution and still not supporting any inheritance. That next-worse solution is the constructor
property which also doesn’t support inheritance. It’s about 2.7x slower than the second-best solution: instanceof
. The instanceof
operator is about 7x slower than the fastest solution: is
.
This leaves is
as by far the fastest solution, the solution with that best supports inheritance, and doesn’t have any awkward conditions like getClass
requiring public
classes. It is definitely the best way to check an object’s type.
Spot a bug? Have a question or suggestion? Post a comment!
#1 by ryzed on June 16th, 2014 ·
Also, “is” works with interfaces like “(someObject is ISomeInterface)”.
#2 by tracasseur on June 18th, 2014 ·
Very interesting article today, I did not know this. Thanks for taking time to research this, many of us appreciate it
#3 by Mars on June 19th, 2014 ·
Thx a lot from China!
#4 by focus on June 27th, 2014 ·
I started to use “is” about 9 years ago, just from my first days of AS3 coding, after few similar performance tests. Wonder why I see it here, in this wonderful blog (which I read many years as well), only now, when lot of as3 coders move out from the platform next to Unity / HTML5 / etc..
#5 by A Flash Corps on June 28th, 2018 ·
What about (parent as Child)!=null ?