package {
import flash.display.Sprite;
public class test1 extends Sprite {
private var tmp:Object;
public function test1() {
createObj(tmp);
if(tmp == null) {
trace("nothing changed");
}
}
private function createObj(obj:Object):void {
obj = new Object();
}
}
}
In the above code the output on the console is :
nothing changed
Why?
If the argument to createObj was passed by reference(which is the
default behavior of actionscript), why did it not get modified?
You don't pass a reference. You are passing null which is assigned to the local variable obj for use within the function.
Passing arguments by value or by reference:
To be passed by reference means that
only a reference to the argument is
passed instead of the actual value. No
copy of the actual argument is made.
Instead, a reference to the variable
passed as an argument is created and
assigned to a local variable for use
within the function.
In createObj you are creating a new reference which you must return:
public function test1() {
tmp = createObj();
if(tmp != null) {
trace("Hello World!");
}
}
private function createObj():Object {
return new Object();
}
Related
I know how to bind a property, but how can I bind the call of a fuction?
For example: I have a ObjectProperty which points to a file. Now, I want to bind the path to its folder? If the value of ObjectProperty is C:\\user\Desktop\text.txt, the bonding should point to C:\\user\Desktop.
I thought I can call getParentFile() within the binding.
There a many ways to map an ObjectProperty, take a look at the class Bindings.
(All examples assume that you have a ObjectProperty<File> file)
Bindings.createObjectBinding(Callable<T> func, Observable... dependencies)
ObjectBinding<File> parent = Bindings.createObjectBinding(() -> {
File f = file.getValue();
return f == null ? null : f.getParentFile();
}, file);
Bindings.select(ObservableValue<?> root, String... steps)
ObjectBinding<File> parent = Bindings.select(file, "parentFile");
This will print a warning on the error-stream when file is null.
You can also create your own mapping method (which is similar to createObjectBinding):
public static <T,R> ObjectBinding<R> map(ObjectProperty<T> property, Function<T,R> function) {
return new ObjectBinding<R>() {
{
bind(property);
}
#Override
protected R computeValue() {
return function.apply(property.getValue());
}
#Override
public void dispose() {
unbind(property);
}
};
}
And use it
ObjectBinding<File> parent = map(file, f -> f == null ? null : f.getParentFile());
Is it possible to call a function in AS3 using a string value as the function name e.g.
var functionName:String = "getDetails";
var instance1:MyObject = new MyObject();
instance1.functionName(); // I know this is so wrong, but it gets the point accross:)
UPDATE
The answer from #Taskinoor on accessing a function is correct:
instance1[functionName]();
And to access a property we would use:
instance1[propertyName]
instance1[functionName]();
Check this for some details.
You may use function.apply() or function.call() methods instead in the case when you dont know whether object has such method for instance.
var functionName:String = "getDetails";
var instance1:MyObject = new MyObject();
var function:Function = instance1[functionName]
if (function)
function.call(instance1, yourArguments)
I have created the following wrappers for calling a function. You can call it by its name or by the actual function. I tried to make these as error-prone as possible.
The following function converts a function name to the corresponding function given the scope.
public static function parseFunc(func:*, scope:Object):Function {
if (func is String && scope && scope.hasOwnProperty(funcName)) {
func = scope[func] as Function;
}
return func is Function ? func : null;
}
Call
Signature: call(func:*,scope:Object,...args):*
public static function call(func:*, scope:Object, ...args):* {
func = parseFunc(func, scope);
if (func) {
switch (args.length) {
case 0:
return func.call(scope);
case 1:
return func.call(scope, args[0]);
case 2:
return func.call(scope, args[0], args[1]);
case 3:
return func.call(scope, args[0], args[1], args[2]);
// Continue...
}
}
return null;
}
Apply
Signature: apply(func:*,scope:Object,argArray:*=null):*
public static function apply(func:*, scope:Object, argArray:*=null):* {
func = parseFunc(func, scope);
return func != null ? func.apply(scope, argArray) : null;
}
Notes
Call
The switch is needed, because both ...args and arguments.slice(2) are Arrays. You need to call Function.call() with variable arguments.
Apply
The built-in function (apply(thisArg:*, argArray:*):*) uses a non-typed argument for the argArray. I am just piggy-backing off of this.
A.as :
public class A {
public function getFunction():Function {
return function():void {
if(this is C) {
trace("C");
} else {
trace("not C");
}
}
}
public function func1():void {
var internalFunc:Function = getFunction();
internalFunc();
}
}
B.as :
public class B extends A implements C {
}
In some other class :
var b:B = new B();
B.func1();
Output is :
"Not C"
I was expecting the trace output to be
"C"
Can someone explain why?
An anonymous function, if called directly, is scoped to the global object. If you trace this inside it, you will see [object global] instead of [object B], as you would, if this refered to b.
A common workaround is using a closure:
var self:A = this;
return function():void {
if(self is C) {
trace("C");
} else {
trace("not C");
}
}
Please note however, the instance-members of a class defining an anonymous function are available from within. This works, because they are resolved at compile time.
edit in response to Amarghosh's question:
Yes, this points to the global object, but that doesn't mean, you cannot access the instance members of the declaring class. This little piece of code should explain the details:
package {
import flash.display.Sprite;
public class Test extends Sprite {
private var foo:String = "foo";
public function Test() {
var anonymous:Function = function ():void {
trace(foo);//foo
trace(this.foo);//undefined
};
anonymous();
}
}
}
greetz
back2dos
A few things with the code that I assume are just typos?
The getFunction() method doesn't return anything and will thus cause a compiler error.
Your call code calls func1() as a static method, not as a method on an instance of the B. This will also cause a compiler error. I believe these are typos.
In my tests, using your modified code. The output is C. There must be something else going on with your code. Here are my mods to A:
public function getFunction():Function {
if(this is C) {
trace("C");
} else {
trace("not C");
}
return getFunction;
}
Here is my mod to the runnable code, which I put in creationComplete of an empty MXML Application file:
var b:B = new B();
b.func1();
I assume your "real world" code is more extensive than the sample and there must be something else going on.
I have many Flex objects like this one:
public class MyData {
public var time: Date;
public var label: String;
}
I am populating this object from a DB record retrieved via AMF that looks something like this:
{
label: "Label",
incident: "2009-08-15 11:12:14.12233"
}
I want to write a generic value mapper for these object that, given a target object (instance of MyData here) and an input record, will be able to tell that MyData.time is a Date field and perform type mapping automatically. Something like this:
function map(obj, targetType): * {
var newInstance: * = new targetType();
for (var property: String in obj) {
if (getPropertyType(targetType, property) == Date) {
newInstance[property] = parseDate(obj[property]);
}
else {
newInstance[property] = obj[property];
}
}
}
function getPropertyType(type_var: Class, property: String): Class {
// .. this is what I have no idea how to do
}
Can someone fill in the blank here?
You possibly need something like describeType. And maybe you need to use getDefinitionByName() if you want to make to a real object. So something like this for the contents of your function:
var typeXml:XML = describeType(type_var[property]);
return getDefinitionByName(typeXml.type[0].#name);
I haven't compiled it. Just throwing it out there to see if it helps.
You can use the 'is' operator to check the type of an object.
The is operator
function map(obj, targetType): * {
var newInstance: * = new targetType();
for (var property: String in obj) {
if (obj[property] is Date) {
newInstance[property] = parseDate(obj[property]);
}
else {
newInstance[property] = obj[property];
}
}
}
hth
Koen
If you need to map an Object variable to a variable class as MyData you can do the following
public class MyData
{
public var time: Date;
public var label: String;
function map(obj:Object):void
{
for (var property: String in obj)
{
this[property] = obj[property];
}
}
}
Note: The object obj must contain the exact "time" and "label" properties.
Hope it solves your problem
In a previous application that I had written, I had a Class that extended AdvancedDataGrid (ADG). It contained the following code:
package
{
public class CustomADG extends AdvancedDataGrid
{
....
// This function serves as the result handler for a webservice call that retrieves XML data.
private function webServiceResultHandler(event:ResultEvent):void
{
var resultXML:XML = new XML(event.result);
dataProvider = new HierarchicalData(resultXML.children);
}
....
public function setOpenNodes(maxDepth:int = 0):void
{
var dataCursor:IHierarchicalCollectionViewCursor = dataProvider.createCursor();
while (dataCursor.current != null)
{
if (dataCursor.currentDepth < maxDepth)
dataProvider.openNode(dataCursor.current);
dataCursor.moveNext();
}
dataProvider.refresh();
}
}
}
In this implementation, the function setOpenNodes() worked fine - it did exactly what I intended it to do - pass it a number, and open all nodes in the dataProvider at or below that level.
Now, I am creating a new ADG Class and want to reproduce this functionality:
package view
{
import mx.collections.IHierarchicalCollectionViewCursor;
public class ReportADG extends AdvancedDataGrid
{
public function ReportADG()
{
super();
}
public function setOpenNodes(maxDepth:int = 0):void
{
var dataCursor:IHierarchicalCollectionViewCursor =
dataProvider.createCursor();
while (dataCursor.current != null)
{
if (dataCursor.currentDepth < maxDepth)
dataProvider.openNode(dataCursor.current);
dataCursor.moveNext();
}
dataProvider.refresh();
}
}
}
The dataProvider is set in the parent component:
<view:ReportADG id="reportADG" dataProvider="{reportData}" />
reportData is set in another file:
reportData = new HierarchicalData(resultXML.children);
However, I am getting runtime errors:
TypeError: Error #1034: Type Coercion failed: cannot convert ListCollectionViewCursor#6f14031 to mx.collections.IHierarchicalCollectionViewCursor.
I've tried casting dataProvider as ICollectionView. I've tried then casting the ICollectionView as IHierarchicalCollectionView. I've tried all sorts of casting, but nothing seems to work. Why won't this work in this new implementation as it did in the past implementation? What do I need to do?
*** Update:
I started debugging this. I added an override setter to my ADG Class to see when dataProvider was being set:
override public function set dataProvider(value:Object):void
{
super.dataProvider = value;
}
I added a breakpoint to this setter and to my setOpenNodes() function. Sure enough, the dataProvider is being set BEFORE setOpenNodes() is called, and it is HierarchicalData. But, when the setOpenNodes() the debugger says that the dataProvider is a null ArrayCollection. It seems like this is the root issue.
I needed to call commitProperties before attempting to access the dataProvider property.
public function setOpenNodes(maxDepth:int = 0):void
{
super.commitProperties();
var dataCursor:IHierarchicalCollectionViewCursor =
dataProvider.createCursor();
while (dataCursor.current != null)
{
if (dataCursor.currentDepth < maxDepth)
dataProvider.openNode(dataCursor.current);
dataCursor.moveNext();
}
dataProvider.refresh();
}