Pass arguments to "new" operator through an array in ActionScript 3 - actionscript-3

How can I achieve the following for any number of elements in the arg array? If it was a function, I'd use Function.apply(), but I can't figure out how to do it with the new operator.
var arg:Array = [1,2];
new MyClass( arg[0], arg[1] );

If you set up your class to accept a list of arguments using ... args you can pass in as many as you like. Then in the constructor you will access them just like a normal array.
class MyClass
{
public function MyClass(... args):void
{
//args is an Array containing all the properties sent to the constructor
trace(args.length);
}
}

Dont pass each element of the array, just pass the array.
var arg:Array = [1,2];
new MyClass(arg);
Then inside of your class, loop through the array.

It is unfortunately not possible, because there is no way to directly access the constructor method of a Class object.
Note: If you'd be using a Function object to make up your class (prototype inheritance), then it would be possible, but i figure, this is not an option for you.
You could work around the problem with a little (ugly) helper method, on which you can read about here: http://jacksondunstan.com/articles/398

As stated in the comments is is not possible to apply settings on the constructor, but you could use this trick to set properties on a new instance of a class (which should be public)
public function setProps(o:Object, props:Object):* {
for (var n:String in props) {
o[n] = props[n];
}
return o;
}
.. use it like this
var args:Object = {x:1, y:2};
var instance:MyClass = setProps( new MyClass(), args ) );
source:
http://gskinner.com/blog/archives/2010/05/quick_way_to_se.html

Related

Pass Variables As Reference AS3

I have a function which has arguments that will modify multiple variables that are global. And I want the arguments to be reference arguments, so they can modify multiple global variables with the same lines of code that are modifying the arguments.
example(psuedocode):
function random(a:number, b:number, c:number):void{
a = RNG(20);
b = RNG(25);
c = RNG(30);
}
there will be two different variables passed in through a, b and c, these are global, but a, b and c are not. The goal is to not have to have identical lines of code for both separate sets of variables to set the RNG numbers.
Edit: So I suppose more explanation is in order I will probably just try to research making a wrapper or other object to add all the variables to, but I just didn't know what type of object to make and how to make it. I admit I was just being a little bit lazy in a little bit too complex creative way.
I have two sets of global variables that I want to pass into this function and set them equal to the same range of RNG as the corresponding ones in each set. The way I'm trying to do this without repeating "a = RNG(20);" twice for each one is by passing the global variables into the function as arguments, but the arguments are the variables that are having the RNG set to them. The only way this can work is if the variables are passed to the function as reference so that setting the RNG to the arguments will change the global variables.
There are two types of data in AS3:
Plain data: Boolean, String, Number, int, uint — always passed as values.
Objects: Object, Array and literally everything else — always passed as a pointer/reference rather than through copy/clone.
There's no trick, like in C/C++ there is, to pass some plain variable as a pointer to let a method modify the original and only value.
That said, there are two ways around.
Solution №1: you can pass variables indirectly, in pairs like container → variable name.
function doIt(A:Object, a:String):void
{
A[a] = RNG(20);
}
Solution №2: devise a custom wrapper class to cross the border between plain and object data.
Implementation:
package
{
public class Oint
{
public var data:int;
// Class constructor.
public function Oint(value:int = 0)
{
data = value;
}
// There's always nice to have a interface methods,
// rather than member or getter/setter, because
// you can actually link to read/write methods.
public function read():int
{
return data;
}
public function write(value:int):void
{
data = value;
}
// With this you can use Oint variables in math expressions.
public function valueOf():Object
{
return data;
}
// With this you can trace Oint variables and see their values.
public function toString():String
{
return data.toString();
}
}
}
Usage:
function random(a:Oint, b:Oint, c:Oint):void
{
a.data = RNG(20);
b.data = RNG(25);
c.data = RNG(30);
}

Inheriting from Array

I am trying to inherit all of Array's methods without using ES6 class syntactic sugar. Additionally, I want methods like new MyArray().map() to return instances of MyArray.
Simple illustration of my problem:
class MyArrayES6 extends Array{}
new MyArrayES6().slice() instanceof MyArrayES6 //true
function MyArray(){}
MyArray.prototype = Object.create(Array.prototype)
MyArray.prototype.constructor = MyArray
MyArray[Symbol.species] = MyArray //Doing this doesn't affect the outcome
new MyArray().slice() instanceof MyArray //false, to my suprise!
A more complete code example
edit: Gave clearer example
The problem is that ArraySpeciesCreate does not use ##species when the object is not an array.
And if you don't use extends Array, instances won't be arrays by default.
If you really want it to work, you can still return a real array with a modified [[Prototype]]:
function ArraySub(){
return Object.setPrototypeOf([], ArraySub.prototype);
}
ArraySub.prototype = Object.create(Array.prototype)
ArraySub.prototype.constructor = ArraySub
ArraySub[Symbol.species] = ArraySub;
console.log( new ArraySub().slice() instanceof ArraySub );
But that will hurt performance so bad. Better use extends Array.

AS3 variable declared as a null function

I have encountered an AS3 function that is declared as a null variable, as in:
public var edgeWeights:Function = null;
I am not sure how to use this function to change null to another value (e.g., a number like 2 or 3). I thought something like cs.edgeWeights = 2 might work, but that creates a compile error, as does cs.edgeWeights(2);
I believe these are anonymous functions in AS3 and I did do some research on them, but could not find a resolution to this situation.
public var edgeWeights:Function = null;
This notation means declaring variable edgeWeights of type Function. In Actionscript Function is an object and can be set to null.
To use it you need to set this variable to some function. For example:
edgeWeights = function(a:int,b:int):int { return a+b } or edgeWeights = Math.sin.
What function you should set there depends on your particular case.
If you assume that the Class that declares edgeWeights is Widget:
protected var widget:Widget;
protected function createWidget():void {
widget = new Widget();
widget.edgeWeights = widgetCallback;
}
//signature would need to match what the Widget
//actually expects this callback to do
protected function widgetCallback():void {
trace('hi from widget callback');
}
Note that it's probably bad practice to have a public callback variable and not provide a default implementation, so if you have access to the source code, you should probably fix that.
Given any function:
public function someFunction()
{
...
}
You can create a "pointer" with this: this.edgeWeights = someFunction; (yes, without ())
Later you just use: this.edgeWeights(); and you'll be calling someFunction().

User-defined type conversions in ActionScript?

Is there any way for me to define implicit or explicit type conversions in ActionScript?
For instance, I want to define a conversion such that Array can cast into MyClass implicitly. If not, an explicit cast would be useful. I know I can always pass it into my constructor, but I am dealing with semantics here, and I am most interested in a conversion solution if it exists.
Type casting in ActionScript 3
Object(instanceOfOtherObject);
Works based on the valueOf property of the given class (if defined). Therefore, you can define your class MyClass as such:
package {
public class MyClass {
private var myArray:Array;
public function MyClass(inputArray:Array = null) {
myArray = (inputArray ? inputArray : new Array());
}
public function valueOf():Array {
return myArray;
}
}
}
Then you will be able to perform this typecasting:
var mc:myClass = new MyClass();
var arr:Array = Array(myClass);
To my knowledge, the reverse is not an option because the valueOf function of Array does not return an object of type MyClass. There is nothing stopping you from creating a CastableArray that extends and overrides the valueOf function of Array to make it return an instance of MyClass using the constructor I defined above, though you may run into other issues with other fundamental language components that expect an Array to return an Array in its valueOf property (comparison of objects comes to mind).
I have not done any particular testing with this next suggestion, but if MyClass extends from Array and does not define a valueOf function, it may still be possible to do the type conversion depending on the constructor of MyClass and what Flash does in circumstances when valueOf is not defined.

Playing nicely with "for each" in ActionScript?

Lets say I have an ActionScript class: MyClass and that class has data in it. Now, lets say I want to iterate over that data using "for each":
var myData:MyClass = new MyClass();
myData.Populate(fromSource);
for each(var item in myData) {
DoSomethingWith(item);
}
Of course, this does nothing, because MyClass is a custom class, and I haven't done anything special to it yet.
What do I need to do to MyClass to make it play nicely with "for each"? Can I hand it an iterator or an enumerator or something?
I believe you need to extend Proxy class and implement nextValue(index:int). It is used by for each.
Ok, I figured it out.
#alxx helped me get to the answer. Here is a complete answer:
public class MyClass extends Proxy
{
override flash_proxy function nextNameIndex (index:int):int {
// This is the command to move to the next item or start over (index == 0)
// return an incremented index when there is data
// return 0 when you are done.
}
override flash_proxy function nextValue(index:int):* {
// This is the command to get the data
// The index that is passed in is the index returned in nextNameIndex
}
}
You should check out the Adobe livedocs page on for each ... in. They have your answer there.
"[for each ... in] Iterates over the items of a collection and executes statement for each item. Introduced as a part of the E4X language extensions, the for each..in statement can be used not only for XML objects, but also for objects and arrays. The for each..in statement iterates only through the dynamic properties of an object, not the fixed properties. A fixed property is a property that is defined as part of a class definition. To use the for each..in statement with an instance of a user-defined class, you must declare the class with the dynamic attribute."