Using . or [ ] to access Object properties - what's the difference? - actionscript-3

What is the difference between the code (i) and (ii) written below ?
(i)
var obj:Object = new Object();
obj.attribute = value ;
(ii)
var obj:Object = new Object();
obj["key"] = value;
Are there any run-time implications if I write this :
var obj:Object = new Object();
obj.somekey = value1 ;
obj["someKey"] = value2 ;
Please explain.

The difference is in the lookup mechanism: If you use the dot syntax, the compiler will know at compile time that you are accessing a property of that object. If you use the bracket syntax, the actual lookup of the property is done at runtime, and there will have to be more type checking - after all, you could compose the key string dynamically, the value could change, or you could even be calling a function instead of a variable, etc.
The result is a significant difference in performance: Bracket syntax takes about three times as long to execute as dot syntax.
Here's a little speed test to illustrate my point:
var start : int = getTimer();
var obj:Object = { something : "something" };
for (var i : int = 0; i < 100000000; i++) {
var n:String = obj.something;
}
trace ("Time with dot syntax: "+(getTimer() - start));
start = getTimer();
for (i = 0; i < 100000000; i++) {
var o:String = obj["something"];
}
trace ("Time with bracket syntax: "+(getTimer() - start));
If the two were the same, except for notation, they should take exactly the same amount of time. But as you can see, this is not the case. On my machine:
Time with dot syntax: 3937
Time with bracket syntax: 9857

Related

Do type of cast affects execution speed?

I wonder if there is any difference in execution speed between:
Object( e.target ).labelTextID
and
MovieClip( e.target ).labelTextID
e.target in that case is a MovieClip, but it doesn't matter.
There aren't any difference. Take a look on this sample code:
var obj:Object = new MovieClip();
var v1:Object = Object(obj);
var v2:MovieClip = MovieClip(obj);
Both type castings produce the same op codes:
_as3_findpropstrict Object
_as3_getlocal <1>
_as3_callproperty Object(param count:1)
_as3_coerce Object
_as3_setlocal <2>
_as3_findpropstrict flash.display::MovieClip
_as3_getlocal <1>
_as3_callproperty flash.display::MovieClip(param count:1)
_as3_coerce flash.display::MovieClip
_as3_setlocal <3>
The conclusion is also confirmed by benchmark:
var obj:Object = new MovieClip();
var v1:Object, v2:MovieClip, i:int;
var t:uint = getTimer();
for(i = 0; i < 1000000; i++)
v1 = Object(obj);
trace("Object: ", (getTimer() - t));
t = getTimer();
for(i = 0; i < 1000000; i++)
v2 = MovieClip(obj);
trace("MovieClip: ", (getTimer() - t));
output:
Object: 92
MovieClip: 90
In terms of what type of object you cast to, I cannot be sure, but in terms of how you are doing your casting I would take a look at this answer: https://stackoverflow.com/a/14268394/1346390
The TLDR of it is, use "as" for casting, since it's both faster and safer, since it throws a TypeError if it fails.
There must be no difference between this cast. In both cases the compiler is instructed to explicit cast the e.target no matter of type of e.target.

As3 Input field for number to convert to array

I have an input field and a list of numbers (10,43,98,10,45,23,10). I want to convert it to an Array, so I can use this array for multiple functions. Please help point me in the right direction.
var a:Array = Array[];
a.push(_numInput.text);//this is my input field name
//trace(a);
var l:Number = 0;
for(var i:uint=0; i< a.length; i++)
{
var t1:Number = l += a[i];
var t2:Number = a.length;
var t3:Number = t1 / t2;
}
trace(t3);
//when i trace the _numInput.text the number show up but i can not use them.
Use split(); to convert string like this to an array.
var a:Array = _numInput.text.split(",");
If you have comma separated numbers in your text field then following is a simplest way to get an array,
var str = "10,43,98,10,45,23,10";
function convertTextFieldStringToArray(s:String):Array
{
var a = s.split(",");
return a;
}
trace(convertTextFieldStringToArray(str));
Spliting a string will just create an array of strings
So what you need to do is then convert that array to an array of int
public function mapIt(obj:Object, index:int, arr:Array):int {
// validation might be a good idea here since this is user imputed text.
return int(obj) // convert to int
}
// your input string
var str:String = "10,43,98,10,45,23,10";
// convert string to array of strings
var arrayOfStrings:Array = str.split(",")
trace( typeof arrayOfStrings[0])// verify first element is a string
trace( arrayOfStrings[0] + arrayOfStrings[0]) // verify string concatination
var arrayOfInt:Array = arrayOfStrings.map(mapIt) // map each element
trace( typeof arrayOfInt[0]) // verify first element is an int
trace( arrayOfInt[0] + arrayOfInt[0]) // verify numbers are correctly added
You need to Cast the String to an Integer!!
a.push(int(_numInput.text));
Edit:Ups, seems i didn't quite understand the question pre-edit

Is it faster to set a reference to a local variable of a nested object in ActionScript or the same?

I have an object that is nested several levels deep which I'm referencing multiple times. Is it faster to create and set a reference to that object or does it matter?
Context
I heard that it is faster to create a local reference. This is years ago and for a Visual Basic project. But this is Flash. And the output is a SWF / bytecode. And the compiler could look at that reference and do what I'm doing so that the object does not have to be looked up each time.
For example:
public function doStuff():void {
Model.instance.view1.button1 = button1;
Model.instance.view1.button2 = button2;
Model.instance.view1.button3 = button3;
Model.instance.view1.button4 = button4;
Model.instance.view1.button5 = button5;
Model.instance.view1.button6 = button6;
// more fake code referencing more something.something.something like things
for (var i:int;i<something.something.something.length;i++) {
var result:Object = Controller.staticMethod(button1);
var result2:Object = Controller.staticMethod(button1);
}
}
Vs:
public function doStuff():void {
var view1:View = Model.instance.view1;
view1.button1 = button1;
view1.button2 = button2;
view1.button3 = button3;
view1.button4 = button4;
view1.button5 = button5;
view1.button6 = button6;
}
.
Taking Baris suggestion I tested it for myself. Below are the results (though I don't know how to interpret them into the actual difference ie 'method 1 is .000001 faster than method 2').
Results
Test 1
var instance:Model = Model.instance;
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
instance.url = "";
}
trace(getTimer()-startTime); // 826, 929, 823
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
Model.instance.url = "";
}
trace(getTimer()-startTime); // 3483, 3976, 3539
Test 2
var instance:Model = Model.instance;
var localLogo:BitmapImage = Model.instance.logo;
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
localLogo = logo;
}
trace(getTimer()-startTime); // 2070, 2083, 2110
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
instance.logo = logo;
}
trace(getTimer()-startTime); // 2028, 2509, 2154
Generally, the as3 compiler is not that smart as we think. I think it does that to reduce compilation times.
This page http://upshots.org/actionscript/20-tips-to-optimize-your-actionscript gives us a rule:
5: The one-dot rule When accessing nested variables, anytime a
variable requires even one level of scope shift to discover, and is
referenced more than once, save it to local variable. In drawing
classes, you’ll often see Math.PI / 2 referenced within loops that
might iterated hundreds of times each frame – that value should be
stored in a local variable. Note that this is not true when
referencing member methods. Saving Math.PI to a variable is
appropriate; saving Math.round to a variable is not.
It does make difference, but to a software building perspective, the more readable code would be the propper way to do it, unless it's critical code, like a physics section of your app.
Other than making time benchmarks, you can inspect the bytecode too, using this tool http://www.swfwire.com/
I prefer :
public function doStuff():void {
var view1:View = Model.instance.view1;
view1.button1 = button1;
view1.button2 = button2;
view1.button3 = button3;
view1.button4 = button4;
view1.button5 = button5;
view1.button6 = button6;
}
I think it looks cleaner, you can profile and see which one is faster but it won't matter performance wise if you are not doing this tons of times in some inner loop.
public function doStuff():void {
var startTime:int = getTimer();
for(var i:int = 0; i<1000000; i++){
Model.instance.view1.button1 = button1;
Model.instance.view1.button2 = button2;
Model.instance.view1.button3 = button3;
Model.instance.view1.button4 = button4;
Model.instance.view1.button5 = button5;
Model.instance.view1.button6 = button6;
}
trace(getTimer()-startTime);
}
public function doStuff():void {
var startTime:int = getTimer();
for(var i:int = 0; i<1000000; i++){
var view1:View = Model.instance.view1;
view1.button1 = button1;
view1.button2 = button2;
view1.button3 = button3;
view1.button4 = button4;
view1.button5 = button5;
view1.button6 = button6;
}
trace(getTimer()-startTime);
}
The time to access is probably irrelevant, because you seem to be doing some UI work, and fractions of milliseconds it will take are of no concern. There are other concerns though:
You are writing repetitious code - this is bad, always. If it must be repeated, you should write a program that repeats it from a single source everywhere, if it may not be repeated - opt not to repeat. The concern is simple. Once you discover that the buttons are in ModelB.instance.view1 you will need to copy and paste the same code multiple times - of course while doing repetitive work you will make a mistake.
AS3 code not only allows, it also welcomes side effects, which, in this case may create undesirable effect. For example, Model.instance.view1 may be a getter that returns a new copy of view every time it is accessed, or, when accessed, it may alter something else in the internal state of the instance, whatever that is, and performing this change multiple times is undesirable.
There's Demeter's rule. When simplified beyond possible, it says that you shouldn't use more than a single dot operator in succession. I.e. an object may only be aware of the property of its own property (or local variable), but never access the property of the property of the property and never further down the line. This imposes unnecessary restriction on the structure of your program. However, sometimes it looks like it might be difficult to avoid, you should strive for a better, self-contained code. More on Demeter's rule here: http://en.wikipedia.org/wiki/Law_of_Demeter
It is not a function of compiler to "optimize" such code, especially because of the side effects. Compiler will faithfully follow the bad code written by a programmer and generate a lot of repetitious assembly because it trusts the programmer that this code is actually needed. Note, I'm not talking about Adobe compiler, which is not an optimizing compiler, and wouldn't remove this code even if the static analysis suggested that. I'm talking about some abstract "ideal" compiler for the language, that would optimally compile the code in question.

AS3 Remove element from array (of objects) generically

Is there a way to generically remove an object from an array?
(maybe not using array.filter or creating a new array)
Example:
var arr:Array= new Array();
//create dummy objs
for (var i:uint=0; i < 10; i++){
var someObject:SomeClassObject = new SomeClassObject();
someObject.Name ="Amit"+ i;
someObject.Site="http://www.mysite.com/"+i;
//...many more props
arr.push(someObject);
}
//
removeElement("Amit4",arr);
removeElement("Amit8",arr);
//...so on so forth
Currently im using array.splice() to remove object
for (var i:Number=0; i < arr.length; i++)
{
if (arr[i].Name == element)
{
arr.splice(i, 1);
}
}
I want to write removeElement in such a way that i can use it for different
types of objects.
currently removeElement becomes dependant on implmentation..
Suppose if i want to remove a file from array of files given file name..i wud have to
again write "removeElement" by changing criteria.
Also may be i can vary the criteria varing criteria?
example :
arr= removeElement("Site","http://www.mysite.com/6",arr)
will remove object from arr whose "Site" property is equal to "http://www.mysite.com/6"
(using above example)
ie. removeElement(criteria:object,criteria_value(s):object,arr)
Thanks All.
Use
if(array.indexOf(obj) != -1)
array.splice(array.indexOf(obj),1);
I think the most flexible approach is the one followed by Array::filter. It's up to the caller to determine whether an item should be filtered out of the list or not, through a function callback.
Now, if you want to do it in place, you could write a simple function like this:
function remove(list:Array,callback:Function):Array {
for(var i:int = list.length - 1; i >= 0; i--) {
if(!callback(list[i])) {
list.splice(i,1);
}
}
return list;
}
This returns the list, as it could be convenient if you wanted to chain calls, but it acts on the array you passed instead of creating a new one.
Also note that it loops backwards. Otherwise, splice will get you bogus results.
You could use it like this:
var arr:Array = [1,2,9,10,455];
trace(arr);
function removeCallback(item:Number):Boolean {
return item < 10;
}
remove(arr,removeCallback);
trace(arr);
This way you are not restricted to equality (or inequality). The caller determines if the item should be kept or removed, by returning true or false respectively (to match filter). So, it's pretty much like filter, except it works in-place. If you want, you could also keep the same interface for the callback (passing the index of the item and a reference to the original array) to make it more coherent.
By the way, you can use strings as indices for an array, and then you can safely use the 'delete' keyword to delete an object from inside the "middle" (there's actually no "middle" in this situation :) of the array.
e.g.:
var arr:Array = new Array();
arr['o1'] = new Object();
arr['o1'].someproperty = true;
arr['o2'] = new Object();
arr['o2'].someproperty = true;
arr['o3'] = new Object();
arr['o3'].someproperty = true;
trace (arr['o2'].someproperty);
//Returns 'true'
trace (arr['o2']);
//Returns '[object Object]'
delete arr['o2'];
trace (arr['o2']);
//Returns 'undefined'
trace (arr['o2'].someproperty);
//Returns 'TypeError: Error #1010: A term is undefined and has no properties.'
The disadvantage is you won't be able to know the length of the array (arr.length will return 0), but you can of-course track it yourself...
Here is a generic function which will do what you want:
public static function removeItem(array: Array, propertyName: String, value: String): Array
{
var newArray: Array = [];
for (var index: int = 0; index < array.length; index++) {
var item: Object = array[index];
if (item && item.hasOwnProperty(propertyName)) {
if (item[propertyName] != value)
newArray.push(item);
}
}
return newArray;
}

AS3 - Is it possible to search an Array by Object properties?

Would it be possible to use Array.indexOf() to search through an Array by the properties of the Objects within the array:
var myArray:Array = new Array();
var myMovieClip = new MovieClip();
myMovieClip.name = "foo";
myArray.push(myMovieClip);
myArray.indexOf(MovieClip.name == "foo"); //0
OR
myArray.indexOf(myMovieClip.name == "foo"); //0
Both indexOf() above do not work, is there away to achieve this with the correct syntax?
Look into Array's filter method (newly available for AS3). You can write a filter method that returns all objects that will meet your criteria (in your case, a movieclip with a certain name)
index of will search for an entry ... MovieClip.name == "foo" should throw a compiler error, since MovieClip does not have a property "name" ... myMovieClip.name == "foo" will be true, and then you will get the index of true, if it is in the array at all ...
if you really need the index, you will need to iterate over the array ... by key ... or in an incremental loop, if the array is numeric and dense ...
if the array is associative (string keys used) you imperatively need to use for-in loops, since filter and related functions will only cover numeric indices ...
in a numeric array, i'd suggest one of the following two approaches:
//this will return an array of all indices
myArray.map(function (val:*,index:int,...rest):int { return (val.name == "foo") ? index : -1 }).filter(function (val:int,...rest):Boolean { return val != -1 });
//here a more reusable utility function ... you may want to put it to some better place ... just as an example ...
package {
public class ArrayUtils {
public static function indexOf(source:Array, filter:Function, startPos:int = 0):int {
var len:int = source.length;
for (var i:int = startPos; i < len; i++)
if (filter(source[i],i,source)) return i;
return -1;
}
}
}
//and now in your code:
var i:int = ArrayUtils.indexOf(myArray, function (val:*,...rest):Boolean { return val.name == "foo" });
hope that helped ... ;)
greetz
back2dos
Though back2dos' method is cool, I think beginners might find it overly-complicated, so for the sake of them here is a simpler method, that may be easier to use, though won't be as versatile for any situation as back2dos' method.
var myArray:Array = new Array();
var myMovieClip1 = new MovieClip();
var myMovieClip2 = new MovieClip();
myMovieClip1.name = "foo";
myMovieClip2.name = "bar";
myArray.push(myMovieClip1);
myArray.push(myMovieClip2);
function getIndexByName(array:Array, search:String):int {
// returns the index of an array where it finds an object with the given search value for it's name property (movieclips, sprites, or custom objects)
for (var i:int = 0; i < array.length; i++) {
if (array[i].name == search) {
return i;
}
}
return -1;
}
trace("bar index = "+getIndexByName(myArray, "bar"));
trace("foo index = "+getIndexByName(myArray, "foo"));
myMovieClip.name == "foo";
^== if you want to assign a variable, use
myMovieClip.name = "foo";
Here is what I did. Change the function name if you want... this is all just for the sake of explanation.
getObjectFromArrayByProperty(arrayToSearch, 'oBjectProperty', 'value');
function getObjectFromArrayByPoperty(array:Array, _property, search:String):Object{
for (var i:int = 0; i < array.length; i++) {
if (array[i][_property] == search) {
return array[i];
}
}
return null;
}
This returns the object instead of just an index. If a match is not found, the function returns null.