Function.call( thisObject ) not working on AS3 - actionscript-3

I have a very simple code here that explains the difference I found between AS2 and AS3 when using the call method of a function.
var a = {name:"a"}
var b = {name:"b"}
function c()
{
trace(this.name)
}
c() // AS2: undefined AS3: root1
c.apply(a) // AS2: a AS3: root1
c.apply(b) // AS2: b AS3: root1
How can I force AS3 to respect the thisObject argument in AS3?
Here is Adobe Documentation
"http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Function.html#apply()"
Thanks

It's important to recognize that Functions are different from Methods. Methods are bound to the objects that they are defined in, whereas Functions are not bound to any object.
When you are using apply or even call on a method you are extracting it from its instance, but it will always be bound to the object.
So in your example if c() is inside an object, that is why you are not seeing thisObject change.
From adobe on Methods:
Methods are functions that are part of a class definition. Once an
instance of the class is created, a method is bound to that instance.
Unlike a function declared outside a class, a method cannot be used
apart from the instance to which it is attached
Now if you want to be able to change the thisObject you can create a function outside of the object and pass the new thisObject parameter. Here's a very basic example:
class myClass {
public function myBoundFunction():void {
trace( "Bound to: " + this );
}
}
//declared outside the class
function unboundFunction():void {
trace( "Unbound: " + this.name );
}
Then instantiating and applying the functions with thisObject parameter:
var c:myClass = new myClass();
//bound function call
c.myBoundFunction.apply( this );
//unbound call
unboundFunction.apply( this );
Output:
Bound to: [object myClass]
Unbound: root1

Related

Are classes methods created individually for instances?

I want to know whether in ActionScript 3 there's a way to share a same function (method) between the instances of a class definition, only referencing the same function everytime... i.e., this example should log true, but logged false (note: I'd want this to reduce duplicating functions).
class A {
function f() {}
}
trace(
(new A).f === (new A).f
)
An ActionScript 3 language specification appears to say that a prototype attribute exists, but not implemented. I've understood that individual classes have a prototype object. I've specially found a prototype property (probably inherited from Class/Object) and wonder if classes use meta functions in order to be constructed (since their type is "object"... when I test with typeof: trace(typeof A, typeof Class, typeof Object)).
My last try:
class A {}
A.prototype.f = function() {}
trace(
(new A).f === (new A).f
)
It says that f doesn't exist. I could define this class as a function instead (in order to move methods to the prototype object):
function A() {}
A.prototype.f = function() {}
, but in this way I can't control access of instance members.
AS3 uses Bound methods to ensure that inside your functions, this always points to original instance object by default.
Imagine that you pass a function f of an instance a to some other object b. When then object b calls function f, the this name still points to a inside scope of the function - this reference has to be store somewhere.
Well, you could assign function to static property of your class:
package adnss.projects.evTest
{
public class A {
private var x:Number = 5;
private var _y:Number = 0;
public function A(){}
static public const f = function (p:Number):Number {
this._y = p;
return this.x*p;
}
public function get y():Number { return _y; }
}
}
And then have access to private properties of the instance of A in that static function by explicitly using this:
var instance:A = new A();
trace("return:", A.f.call(instance, 3)); //return: 15
trace("instace:", instance.y); //instance: 3
But that is kind of tricky for possible benefits (if any).
And about prototypes. You basically don't use them in AS3. Is't there like a souvenir :) - read this for more info

AS3: Loading swf as Custom Class that extends MovieClip - getting null object reference

I followed the example from a previous question and I am loading an external swf using a loader and inside the loader event handler I am trying to cast the loader.content as my custom class PanelReferenceClip which extends MovieClip
When I publish I receive a this error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
Just to make sure and test that the swf location was correct and the swf was actually being loaded, I changed the type of the content to as MovieClip and it worked fine.
EDIT: I also wanted to add that these swfs are being stored locally and not being pulled across the internet, multiple networks or servers.
I am not sure if I did something quirky in my class so I am providing the source to my custom class PanelReferenceClip
package com.components
{
import com.UI.DevicePanel;
import flash.display.MovieClip;
/**
* ...
*
* used to store the loaded swf inside the panel
*
* *parentPanel is set so that it is able to access it's parent Panel when needing
* to set parameters.
*/
public class PanelReferenceClip extends MovieClip
{
private var _parentPanel:DevicePanel;
private var _bg_mc:MovieClip;
private var _oldY:Number = 0;
private var _oldX:Number = 0;
private var _IsDragging:Boolean = false;
public function PanelReferenceClip() {
super();
}
/*--------------------------------------------------------------------------
* GETTERS AND SETTERS
* -----------------------------------------------------------------------*/
public function set parentPanel(p:DevicePanel):void {
_parentPanel = p;
}
public function get parentPanel():DevicePanel {
return _parentPanel;
}
public function get bg_mc():MovieClip {
try {
return getChildByName("bg_mc") as MovieClip;
} catch (e:Error) {
trace("could not find bg_mc in " + _parentPanel.DeviceName + " panel");
}
return null;
}
public function set oldY(n:Number):void {
_oldY = n;
}
public function get oldY():Number {
return _oldY;
}
public function set oldX(n:Number):void {
_oldX = n;
}
public function get oldX():Number {
return _oldX;
}
public function set IsDragging(b:Boolean):void {
_IsDragging = b;
}
public function get IsDragging():Boolean {
return _IsDragging;
}
}
}
Here is the part of another class that is loading the swfs and then trying to assign them as the class prop _reference which is of type PanelReferenceClip . I am doing this so I am able to get ahold of the swf and it's children because when you import a swf you do not get to set the instance name of the imported swf. So I am assigning it a custom class that extends MovieClip so I can store have some custom properties.
private function handleLoad(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, handleLoad, false);
// keep reference to the content
_reference = e.target.content as PanelReferenceClip;
// ** BREAKS ON THE NEXT LINE **/
trace(_reference.numChildren);
// add loader to the display list so we can see the external SWF.
addChild(e.target.loader);
// signal the sim engine that the swf has loaded
// and to go ahead and wire up the components
dispatchEvent(new DataEvent(DataEvent.COMPLETE));
initPanel();
}
Here is the method used to load the swf. I added in the application context part to try it out but I am still not getting anywhere.
public function loadSWF(theSWF:String):void
{
var url:String = theSWF;
var urlReq:URLRequest = new URLRequest(url);
_urlError = url;
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoad);
_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
_loader.load(urlReq,context);
_loader.mouseEnabled = false;
}
This might be caused by converting between types that can't be typecast. Instead of an error occurring when performing someVariable as OtherClass, the variable will just become null. (Example Shown)
I would store a reference to the original movieclip as one of the properties in PanelReferenceClip, and just use that reference when you need to access things like .numChildren
//MovieClips are subclasses of Sprites... this conversion works
var originalMC:MovieClip = new MovieClip();
var sprite:Sprite = originalMC as Sprite;
trace("sprite: " + sprite); //defined
//Sprites are not subclasses of MovieClips... this conversion wont work
var originalSprite:Sprite = new Sprite();
var mc:MovieClip = originalSprite as MovieClip;
trace("mc: " + mc); //null
//MovieClips are not subclasses of PanelReferenceClips (quite the opposite)
//this conversion wont work, just as the one before
var panelRef:PanelReferenceClip = originalMC as PanelReferenceClip;
trace("panelRef: " + panelRef); //null
As mentioned by #nox-noctis, the problem may be due to the applicationDomain. I answered a similar question here.
The underlying cause of the problem is that there are actually two different classes defined for PanelReferenceClip. Even though they have they may have same name, and contain the same code, Flash sees them as two different objects.
If you give the classes two different names, this will more clearly convey how the classes appear to Flash. In essence you are telling Flash to cast one object type to a different type, eg:
var foo:Foo = new Foo();
var bar:Bar = foo as Bar(); // where Bar does not inherit Foo
What would work though, is to case the panel as a MovieClip, since the panel does extend MovieClip. That would let you add it to the display list, but won't provide an API to the panel's implementation.
One solution is to specify an interface for the classes. This tells Flash that even though the code may be different, the two classes can be used in the same way. This also has the advantage that you only need to compile the interface into the parent SWF, and not the whole class, reducing the file size of the parent.
An interface for the panel might look like this:
public interface IPanelReference
{
function set parentPanel(p:IDevicePanel):void;
function get parentPanel():IDevicePanel;
function get bg_mc():MovieClip;
function set oldY(n:Number):void;
function get oldY():Number;
function set oldX(n:Number):void;
function get oldX():Number;
function set IsDragging(b:Boolean):void;
function get IsDragging():Boolean;
}
Apply the interface to the panel like this:
public class PanelReferenceClip extends MovieClip implements IPanelReference
{
...
}
The parent would then reference the loaded class by the interface and known ancestors:
private function handleLoad(e:Event):void
{
...
_reference = e.target.content as IPanelReference;
trace((_reference as DisplayObjectContainer).numChildren);
trace(_reference.oldX);
addChild(_reference as DisplayObject);
....
}
The facts provided are not conclusive, as it is very important where is the loader SWF and from where does it load external SWFs.
My guess: your loaded content resides in a different ApplicationDomain. See LoaderContext.applicationDomain and second parameter to Loader.load() method.
Some details on the mechanics. Apparently, you compile PanelReferenceClip class into two different SWF files. When you load some external SWF file with code in it, VM decides whether to mix or not any incoming declarations with those of loader SWF depending on loader context you provide. If the context you specified allows mixing, then incoming SWF uses the same declarations with coinciding qualified names that parent SWF has. If not -- the classes being initialized are different even if their fully qualified names are identical. In the latter case you will not be able to cast loaded content to what you like.
Try the following tests in handleLoad() method:
trace(getQualifiedClassName(e.target.content)); // This is the incoming declaration
trace(getQualifiedClassName(PanelReferenceClip)); // This is the parent declaration
Even if the output is identical, that doesn't necessarily mean, that the classes are the same. If you are using some kind of debugger, you may look at the memory addresses in the following test lines.
var incomingClass:Class = e.target.content["constructor"];
var residentClass:Class = PanelReferenceClip;
trace(incomingClass == residentClass); // toggle breakpoint here and compare memory addresses

How to pass arguments into event listener function in flex/actionscript?

Since when using sql lite if you try and do a function at the same moment it throws an error, im just trying to make a function that will check if its executing, and if it is try again in 10 milliseconds, this exact function works fine if i dont have to pass any arguments to the function but im confused how I can pass the vars back into the function it'll be executing.
I want to do:
timer.addEventListener(TimerEvent.TIMER, saveChat(username, chatBoxText));
But it will only allow me to do:
timer.addEventListener(TimerEvent.TIMER, saveChat);
It gives me this compile error:
1067: Implicit coercion of a value of
type void to an unrelated type
Function
How can I get this to pass this limitation?
Here's what I've got:
public function saveChat(username:String, chatBoxText:String, e:TimerEvent=null):void
{
var timer:Timer = new Timer(10, 1);
timer.addEventListener(TimerEvent.TIMER, saveChat);
if(!saveChatSql.executing)
{
saveChatSql.text = "UPDATE active_chats SET convo = '"+chatBoxText+"' WHERE username = '"+username+"';";
saveChatSql.execute();
}
else timer.start();
}
A function called by a listener can only have one argument, which is the event triggering it.
listener:Function — The listener function that processes the event.
This function must accept an Event
object as its only parameter and must
return nothing, as this example
shows:
function(evt:Event):void
Source
You can get around this by having the function called by the event call another function with the required arguments:
timer.addEventListener(TimerEvent.TIMER, _saveChat);
function _saveChat(e:TimerEvent):void
{
saveChat(arg, arg, arg);
}
function saveChat(arg1:type, arg2:type, arg3:type):void
{
// Your logic.
}
Another thing you can do create a custom event class that extends flash.events.Event and create properties that you need within.
package
{
import flash.events.Event;
public class CustomEvent extends Event
{
// Your custom event 'types'.
public static const SAVE_CHAT:String = "saveChat";
// Your custom properties.
public var username:String;
public var chatBoxText:String;
// Constructor.
public function CustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false):void
{
super(type, bubbles, cancelable);
}
}
}
Then you can dispatch this with properties defined:
timer.addEventListener(TimerEvent.TIMER, _saveChat);
function _saveChat(e:TimerEvent):void
{
var evt:CustomEvent = new CustomEvent(CustomEvent.SAVE_CHAT);
evt.username = "Marty";
evt.chatBoxText = "Custom events are easy.";
dispatchEvent(evt);
}
And listen for it:
addEventListener(CustomEvent.SAVE_CHAT, saveChat);
function saveChat(e:CustomEvent):void
{
trace(e.username + ": " + e.chatBoxText);
// Output: Marty: Custom events are easy.
}
Actually, you can pass additional parameters to an event listener without creating a custom event by using Actionscript's dynamic function construction.
private function addArguments(method:Function, additionalArguments:Array):Function
{
return function(event:Event):void {method.apply(null, [event].concat(additionalArguments));}
}
When setting up the closeHandler for the Alert window we call the addArguments() method and pass in an array continaing all of the parameters we want to pass to the closeHandler. The addHandler() method will return the function we will call when the Alert window closes with the parameters included.
protected function btnOK_clickHandler(event:MouseEvent):void
{
Alert.show("Would you like to reverse the text you just entered?", "", Alert.YES | Alert.NO, null, addArguments(alertCloseHandler, [txtInput.text]), null, Alert.YES);
txtInput.text = "";
}
1067: Implicit coercion of a value of type void to an unrelated type Function
Pay due attention to the error you got: it says that a Function is a type and that addEventListener() wants it. Although your listener returns void, it is a Function! So, what about returning the listener?
timer.addEventListener(TimerEvent.TIMER, saveChat(username, chatBoxText));
function saveChat(username:String, chatBoxText:String):Function {
return function(e:TimerEvent):void {
// Do everything that uses "e", "chatBoxText" and "username" here!
};
}
Simple like this. It works for any kind of event. And no closure issues.
Note on removeEventListener():
Don't try to do timer.removeEventListener(TimerEvent.TIMER, saveChat(username, chatBoxText)). It won't recognize your function because you'll be passing a new one on it every time you use saveChat(). Instead, just take its reference out into a variable and you're done:
var functionSaveChat:Function = saveChat(username, chatBoxText);
timer.addEventListener(TimerEvent.TIMER, functionSaveChat);
//trace(timer.hasEventListener(TimerEvent.TIMER));
timer.removeEventListener(TimerEvent.TIMER, functionSaveChat);
//trace(timer.hasEventListener(TimerEvent.TIMER));
You can try this:
var timerHandler:Function = function (event:TimerEvent):void
{
saveChat(username,chatBoxText,event);
}
timer.addEventListener(TimerEvent.TIMER, timerHandler);
Above calling saveChat(arg, arg, arg) it's not for me, i need to pass arguments that i dont have in this method but i have another solution
Im always using additional method passParameters to adding new arguments:
public function passParameters(method:Function,additionalArguments:Array):Function
{return function(event:Event):void{
method.apply(null, [event].concat(additionalArguments));}
}
explanation of this is here - its simple and always work
http://sinfinity.pl/blog/2012/03/28/adding-parameters-to-event-listener-in-flex-air-as3/

What is 'this' in anonymous event handlers in AS3

Good day!
I noticed that in anonymous event handler this is referenced to global, not my class. I know that I can use outer referenced variables (because it creates closures), but how to get right this context?
Simple example:
_movieClipClassVariable = new MyCustomSpriteSubclass();
_movieClipClassVariable.addEventListener(MouseEvent.CLICK, function(event:Event):void {
trace(this); //gives 'global'
});
What about memory usage and garbage collection objects with anonymous handlers? Is declaring handlers as class method better?
I think most of the time declaring a method inside the class is the better choice, (because you don't have to think about the scope, to remove listeners, to not getting accidentally garbage collected, ...) however there are cases where an anonymous function might be a concise and clear approach. It depends.
For instance:
public class XYZ extends Sprite {
...
private function renderBtn() : void {
var btn : SomeButton = new SomeButton();
var ref : XYZ = this;
btn.addEventListener(MouseEvent.CLICK, function(event:Event):void {
trace("XYZ instance: " + ref);
btn.removeEventListener(MouseEvent.CLICK, arguments.callee);
removeChild(btn);
proceed();
});
addChild(btn);
}
private function proceed() : void {
...
Yes you should really declare a class to not end up in thin air.
The delegating technique you used there is mostly used in Javascript. As a flash developer I'd recommend naming the function and keeping it all together in a class.
As for the garbage collection, you'd need to removeEventListener with the exact same syntax than the addEvenListener in order to free it for garbage collection.
HTH
Instead of adding the functions declaration right inside the Event listener, declare the method in your class.
class myTestClass
{
private function listenForEvents():void
{
_movieClipClassVariable = new MyCustomSpriteSubclass();
_movieClipClassVariable.addEventListener(MouseEvent.CLICK, onClipClassClickHandler);
}
private function onClipClassClickHandler(event:MouseEvent):void
{
trace(this); // this is the instance of current "myTestClass"
}
}
More details on anonymous handlers:
http://www.ultrashock.com/forums/actionscript/anonymous-functions-remove-listener-121738.html

AS3 reflection. How to find out if a method is overridden?

Is it possible to use AS3 reflection to find out if a method was overridden?
I need a method like:
protected function isOverriden(methodName:string) : bool
{
//magic here!
//...
return awesomeLocalVariable;
}
So, I pass in the method name as a string and the isOverridden method yields true only if and only if the object has a method of that name and it is overridden from its original implementation.
Any idea on how to code the magic there?
Thanks.
Edit: As requested, the context of the problem:
I'm building a framework for creating AS3 games. I want to provide "components" for my game objects, each component provides functionality to the game object it is applied. Components are based on events (onClick, onUpdate, onShapeCollision, etc) I need this code in the Component class, so I can register only the events that the actual Component-derived class implements (overrides).
Example component:
public class CTrace extends ScriptComponent
{
public override function onClick(event:MouseEvent = null):void
{
trace(Owner.Id);
}
}
The framework should register the onClick method as the event handler for the MouseEvent.CLICK event because it overrides the default implementation.
Why do I need the default implementation? Because I want the classes to override the supported methods so there will be a compile time error if the user tries to use an unsupported event.
Does that makes sense?
Here is a try. The function is static and it may be used to check any class or object regardless of the class in which it is implemented. If you give it the type, it will use it, if you give it an instance, it will get the type by itself. The inner logic is just to check the given type description for the function we are looking for, if such exists and is declared by the class, it will check if the method also exists in the parent. And if both exists, enjoy, it means it is overridden.
/**
* Returns true only if the method name given is declared by
* the source class, and any parent class.
*/
static public function isOverridden(source:*, methodName:String):Boolean {
var parentTypeName:String = getQualifiedSuperclassName(source);
if (parentTypeName == null) {
return false;
}//if
var typeName:String = getQualifiedClassName(source);
var typeDesc:XML = describeType(getDefinitionByName(typeName));
var methodList:XMLList = typeDesc.factory.method.(#name == methodName);
if (methodList.length() > 0) {
//Method exists
var methodData:XML = methodList[0];
if (methodData.#declaredBy == typeName) {
//Method is declared in self
var parentTypeDesc:XML = describeType(getDefinitionByName(parentTypeName));
var parentMethodList:XMLList = parentTypeDesc.factory.method.(#name == methodName);
return parentMethodList.length() > 0;
}//if
}//if
return false;
}//isOverridden
And just in case it is needed, the imports required for it to work:
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
import flash.utils.getQualifiedSuperclassName;
And to use it:
trace(isOverridden(ChildrenClass, "overriddenMethod")); //true
trace(isOverridden(ChildrenClass, "onlyChildMethod")); //false
trace(isOverridden(ChildrenClass, "onlyParentMethod")); //false
If you are asking inside the same object you can
overriden = (this[stringNameOfMethod] instanceOf Function && super[stringNameOfMethod] instanceOf Function);
If not, try using describeType. Check if there's a method with the name and check the "declaredBy" attribute. Voila!
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/package.html#describeType()
Sorry, I'm in the middle of a migration from CS3 to Flash builder so for now I cannot ensure my ideas work correctly. But I will be back.