Referencing getter/setter functions in actionscript 3 - actionscript-3

How does one get a reference the the getter and setter functions in actionscript 3?
if a method is defined on the calls, e.g.
public function blah():String { ...}
I can get a reference to it by just saying blah or this.blah
How do get a reference to
public function get blah2():String {}
public function set blah2(b:String):void {}
Thanks!

Original response:
Unfortunately, you will not be able to store references to those as functions. The getter and setter methods are actually built around the idea that you shouldn't be able to and they therefore function as a property.
Is there a reason that you need to reference the functions specifically?
The comment I'm responding to:
I want to dynamically add external interface methods based on custom metadata tags, e.g. [External]. I was able to do this for the regular methods, but I'm trying to extend this to getter/setters as well. To do this, I need to get a reference to the function dynamically, so I can execute it with the right args using the apply function.
I think you're better off using a multi-step approach in that case. Since getters and setters function as a property and not a method, it would make sense to test to see if it is a property and then simply assign it a value directly. Would you be able to use this:
if( foo.blah2 is Function )
{
foo.blah2.apply( foo, arr );
}
else
{
foo.blah2 = arr[ 0 ];
}

Related

Run a 'constructor' or function, after class fields initialized, in a sane way?

I'd like to use ES6 public class fields:
class Superclass {
constructor() {
// would like to write modular code that applies to all
// subclasses here, or similarly somewhere in Superclass
this.example++; // does NOT WORK (not intialized)
//e.g. doStuffWith(this.fieldTemplates)
}
}
class Subclass extends Superclass {
example = 0
static fieldTemplates = [
Foo,
function() {this.example++},
etc
]
}
Problem:
ES6 public fields are NOT initialized before the constructors, only before the current constructor. For example, when calling super(), any child field will not yet have been defined, like this.example will not yet exist. Static fields will have already been defined. So for example if one were to execute the code function(){this.example++} with .bind as appropriate, called from the superclass constructor, it would fail.
Workaround:
One workaround would be to put all initialization logic after all ES6 public classes have been properly initialized. For example:
class Subclass extends Superclass {
example = 0
lateConstructor = (function(){
this.example++; // works fine
}).bind(this)()
}
What's the solution?
However, this would involve rewriting every single class. I would like something like this by just defining it in the Superclass.constructor, something magic like Object.defineProperty(this, 'lateConstructor', {some magic}) (Object.defineProperty is allegedly internally how es6 static fields are defined, but I see no such explanation how to achieve this programatically in say the mozilla docs; after using Object.getOwnPropertyDescriptor to inspect my above immediately-.binded-and-evaluated cludge I'm inclined to believe there is no way to define a property descriptor as a thunk; the definition is probably executed after returning from super(), that is probably immediately evaluated and assigned to the class like let exampleValue = eval(...); Object.defineProperty(..{value:exampleValue})). Alternatively I could do something horrible like do setTimeout(this.lateConstructor,0) in the Superclass.constructor but that would break many things and not compose well.
I could perhaps try to just use a hierarchy of Objects everywhere instead, but is there some way to implement some global logic for all subclasses in the parent class? Besides making everything lazy with getters? Thanks for any insight.
References:
Run additional action after constructor -- (problems: this requires wrapping all subclasses)
Can I create a thunk to run after the constructor?
No, that is not possible.
How to run code after class fields are initialized, in a sane way?
Put the code in the constructor of the class that defines those fields.
Is there some way to implement some global logic for all subclasses in the parent class?
Yes: define a method. The subclass can call it from its constructor.
Just thought of a workaround (that is hierarchically composable). To answer my own question, in a somewhat unfulfilling way (people should feel free to post better solutions):
// The following illustrates a way to ensure all public class fields have been defined and initialized
// prior to running 'constructor' code. This is achieved by never calling new directly, but instead just
// running Someclass.make(...). All constructor code is instead written in an init(...) function.
class Superclass {
init(opts) { // 'constructor'
this.toRun(); // custom constructor logic example
}
static make() { // the magic that makes everything work
var R = new this();
R.init(...arguments);
return R;
}
}
class Subclass extends Superclass {
subclassValue = 0 // custom public class field example
init(toAdd, opts) { // 'constructor'
// custom constructor logic example
this.subclassValue += toAdd; // may use THIS before super.init
super.init(opts);
// may do stuff afterwards
}
toRun() { // custom public class method example
console.log('.subclassValue = ', this.subclassValue);
}
}
Demo:
> var obj = Subclass.make(1, {});
.subclassValue = 1
> console.log(obj);
Subclass {
subclassValue: 1
__proto__: Superclass
}

Is it not possible to get the arguments array from a static method?

I'm trying to get the reserved keyword arguments array from inside a static method and I'm getting this error:
1042: The this keyword can not be used in static methods. It can only
be used in instance methods, function closures, and global code.
Here is my code:
public static function doSomething(message:String, ...Arguments):void {
var object:Object = this.arguments.caller;
}
If I take the this keyword out then I get the following error:
1120: Access of undefined property arguments.
this is reserved to reference the current instance of a class which unfortunately doesn't exist inside a static function (since static function is not tied to an instance).
You could try using the new rest keyword if you want to pass in an unknown number of arguments:
ActionScript 3.0 includes a new ...(rest) keyword that is recommended instead of the arguments class.
However if you want it just to get the caller function:
Unlike previous versions of ActionScript, ActionScript 3.0 has no arguments.caller property. To get a reference to the function that called the current function, you must pass a reference to that function as an argument. An example of this technique can be found in the example for arguments.callee.
public function test() {
doSomething("Hello", arguments.callee);
}
public static function doSomething(message:String, caller:Function):void {
var object:Object = caller;
}
You could get the arguments of a static method. From the documentation:
Within a function's body, you can access its arguments object by using the local arguments variable.
You do not need the this keyword, this references to the Class instance instead to the function itself:
public static function doSomething():void {
return arguments;
}
Next you can access to the arguments calling the static method:
var arguments:Object = MyClass.doSomething();
trace( arguments.callee );
But remember, like #MartinKonecny said, in AS3 is better use the ...rest keyword or pass a function reference as an argument.
The arguments object is available in static functions but is not available when using the ...rest parameter.
Use of this parameter makes the arguments object unavailable. Although
the ... (rest) parameter gives you the same functionality as the
arguments array and arguments.length property, it does not provide
functionality similar to that provided by arguments.callee. Make sure
you do not need to use arguments.callee before using the ... (rest)
parameter.
Take out the ...rest parameter and the arguments object appears.
Also, the this keyword is not always necessary.
method.apply(this, args);
may throw an error in a static function but the parameter is optional so this also works:
method.apply(null, args);
More on the rest keyword.

When using the 'Class' datatype, how can I specify the type so I only accept subclass of a specific class?

I've got a method that accepts a parameter of type Class, and I want to only accept classes that extend SuperClass. Right now, all I can figure out to do is this, which does a run-time check on an instance:
public function careless(SomeClass:Class):void {
var instance:SomeClass = new SomeClass();
if (instance as SuperClass) {
// great, i guess
} else {
// damn, wish i'd have known this at compile time
}
}
Is there any way to do something like this, so I can be assured that a Class instance extends some super class?
public function careful(SomeClass:[Class extends SuperClass]):void {
var instance:SuperClass = new SomeClass();
// all is good
}
If you are going to instantiate it anyway, why not accept an object instead which allows you to type it to :SuperClass?
careless(SomeClass);
//vs.
careless(new SomeClass);
Not too much of a problem there as far as your code goes.
There are a few differences though:
The object has to be created, because an object is required. If your function does not instantiate the class under some circumstances, this can be a problem. Additional logic to pass either an object or null can bloat the function call.
If you cannot call the constructor outside that function, it won't
work either.
All that is solved by the factory pattern. Pass a factory as the parameter that produces SuperClass objects.
function careful(factory:SuperClassFactory)
Your requirements:
I want to only accept classes that extend SuperClass
and
I need to pass in a Class so that it can be instantiated many times
by other objects later
Can be met by passing in an instance of the class you need, and using the Object.constructor() method.
public function careful(someInstance:SuperClass):void {
//you probably want to store classRef in a member variable
var classRef: Class = someInstance.constructor();
//the following is guaranteed to cast correctly,
//since someInstance will always be a descendant of SuperClass
var myInst:SuperClass = new classRef() as SuperClass;
}
More reading here.
You can't do that in ActionScript 3. In languages like C# you can do something like (forgive me if the syntax is off):
public void Careless<T>() where T : SuperClass
But AS3 does not have 'generics'. Unfortunately the only way I know how to do what you want is the way you have already done.
A pattern that might be more suitable for your use case might be something like:
class SuperClass
{
public static function careless():void
{
var instance:SuperClass = new SuperClass();
// ...
}
}
The only way to have static type checking in ActionScript 3 is to provide an instance of a class.
It is possible but it's expensive. You can use on a Class (not instance) the:
flash.utils.describeType
You then get an XML with a bunch of information including inheritance for that class. Like I said it's an expensive process and probably creating an instance and checking it will be in most cases faster.

How do i refer to a get function as an object

I'd like to reference a get function as a Function object rather than as the value that it returns.
Normally i would be able to simply refer to the function without parenthesizes like so:
private function getFoo():int {
return 0;
}
trace(getFoo); // traces function
But the whole point of get functions is that you can call the function without the parenthesizes, so i just get a return of 0 if i do this:
private function get foo():int {
return 0;
}
trace(foo); // traces 0
Is there be any way at all to grab a reference to the foo function object?
Your first example gets a reference to the function (as it traces Function).
There is no way to get a reference to a getter, as getters are not simple functions, but a representation of a (custom) property of that object. They are not meant to work as a standard ones and so they are not meant to be referenced.
I cannot imagine why would you want to get a reference to that getter? And also, getters are not meant to be used only because you can skip those two symbols ()..

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."