What is the use in having the valueOf() function? - actionscript-3

Why is the valueOf() function present in everything in AS3? I can't think of an instance when this isn't redundant. In terms of getting a value, x and x.valueOf() are completely the same to me (except that one probably takes more CPU cycles). Furthermore even though they may not be the same in terms of setting something, x.valueOf() = y (if even legal) is just completely pointless.
I am confident though that this is here for a reason that I'm just not seeing. What is it? I did try Googling for a minute. Thanks!

As you say, its completely redundant.
The valueOf method is simply included so that ActionScript 3 complies with the ECMA language specification (obviously there are other requirements to be an ECMA language - i believe toString is another example).

Returns the primitive value of the specified object. If this object does not have a
primitive value, the object itself is returned.
Source: Adobe AS3 Reference http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Object.html#valueOf()
Edit:
A primitive value can be a Number, int, bool, etc... They are just the value. An object can have properties, methods, etc.
Biggest difference, in my opinion though:
primitive2 = primitive1;
In this example, primitive 2 contains a copy of the data in primitive 1.
obj2 = obj1;
In this one, however, ob2 points to the same object as obj1. Modify either obj1 or obj2 and they both reflect the change, since they are references.
In short, valueOf is used when you want to see the primitive representation of an object (if one exists) rather than the object itself.

Here is a clear example between
Value Vs. ValueOf:
Value = Thu Jan 2 13:46:51 GMT-0800 2014 (value is date formatted)
ValueOf = 1388699211000 (valueOf is in Raw epoch)

valueOf isn't useless. It allows an Object to provide a value for an expression that expects a primitive type. It's available in AS3 as well as JavaScript.
If someone wrote a function that takes an int, you could pass it your object (more precisely, it passes the result of your object's valueOf() function).
The usefulness is tempered by 1) the fact that the Object isn't passed, so it's only an Object in the outermost scope, and 2) the fact that it's a read-only operation, no assignment can be made.
Here're a couple concrete examples off the top of my head:
Example 1: A Counter class that automatically increments its value every time it's read:
class Counter
{
private var _cnt:int = 0;
public function Counter() { }
public function valueOf():int
{
return _cnt++;
}
public function toString():String { return ""+valueOf(); }
}
Usage:
var c:* = new Counter();
trace(c); // 0
trace(c); // 1
trace(2*c+c); // 2*2+3 = 7
trace(c); // 4
Notes:
I added the toString() pass-through, since functions that take String prefer toString over valueOf.
You must type c as * and not Counter, otherwise you'll get a compiler error about implicit coercion of Counter to Number.
Example 2: A (read only) pointer type
Let's say you have an array of ints, and you want to have a reference (aka pointer) to an element in the array. ECMA scripts don't have pointers, but you can emulate one with valueOf():
class ArrayIntPointer
{
private var arr:Array;
private var idx:int;
public function ArrayIntPointer(arr:Array,
idx:int)
{
this.arr = arr;
this.idx = idx;
}
public function valueOf():int
{
return arr[idx];
}
public function toString():String { return ""+valueOf(); }
}
Usage:
var arr:Array = [1, 2, 3, 4, 5];
var int_ptr:* = new ArrayIntPointer(arr, 2);
// int_ptr is a pointer to the third item in the array and
// can be used in place of an int thanks to valueOf()
trace(int_ptr); // 3
var val:int = 2*int_ptr+1;
trace(val); // 7
// but it's still an object with references, so I
// can change the underlying Array, nand now my
// object's primitive (aka, non-Object types) value
// is 50, and it still can be used in place of an int.
arr[2] = 50;
trace(int_ptr); // 50
// you can assign int_ptr, but sadly, this doesn't
// affect the array.
That's pretty slick. It'd be really slick if you could assign the pointer and affect the array, but unfortunately that's not possible, as it assigns the int_ptr variable instead. That's why I call it a read-only pointer.

Related

Returning by reference from struct method in D

I'm starting my journey in D from C++. In C++ passing by reference or value is quite explicit, but in D it seems to vary between structs and classes.
My question is how can I force a return by reference?
I have a simple XmlNode class for building Xml trees (which is a lift from my C++ code):
import std.stdio;
struct XmlNode
{
string _name;
string _data;
XmlNode[] _children;
this(string name, string data="")
{
_name = name;
_data = data;
}
//Trying to return a reference to the added Node
ref XmlNode addChild(string name,string data = "")
{
_children ~= XmlNode(name,data);
return _children[$-1];
}
string toString(bool bPlain = true, string indent = "")
{
//Omitted for brevity
}
}
And here is the testing code:
int main()
{
auto root = XmlNode("root");
//Chained call
root.addChild("Level 1").addChild("Level 2","42");
//Call in two parts
auto n = root.addChild("Level 1");
n.addChild("Level 2","101"); //n seems to be a copy not a reference
//Chained call
root.addChild("Level 1").addChild("Level 2","999");
writeln(root.toString(false));
return 0;
}
which gives the following output:
root
Level 1
Level 2
42
Level 1
Level 1
Level 2
999
As you can see the 'chained' use of addChild() performs as hoped. But if I try to break it up into two separate calls, only the first has an effect, and the second seems to operate on a copy of the first, not a reference. I optimistically added a ref qualifier to the addChild() signature, but that doesn't seem to help.
As ever, I'd be grateful for any advice (using DMD / Visual D / Visual Studio / Windows 10).
auto n = root.addChild("Level 1");
Here, though addChild returns a reference, it is assigned to a variable, and thus dereferenced and copied. Instead, you probably want:
auto n = &root.addChild("Level 1");
Note that D does not have reference variables, like in C++. Variables can be only pointers (though it's possible to write a wrapper template with reference-like semantics).
Also note that in the current design of XmlNode, the returned reference will only be valid until the next time _children is modified (as that may cause a reallocation and thus move the contents to another address, making any extant references outdated). It is a common footgun, which could be avoided by storing references of XmlNode (or making it a reference type i.e. a class), at the cost of extra dereferences and allocations.

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);
}

AS3 Cast Vector to Array

var leaderboardRowVOs:Vector.<LeaderboardRowVO> = new Vector.<LeaderboardRowVO>();
goes to another part of the system as an Object, and I'm trying to cast it back to actual type
notification.getBody() as Vector.<LeaderboardRowVO> //throwing error
There are two ways of type casting in AS3:
// Casting
// 1: returns null if types are not compatible,
// returns reference otherwise
notification.getBody() as Vector.<LeaderboardRowVO>
// Converting
// 2: throws exception if types are not compatible,
// returns reference otherwise
Vector.<LeaderboardRowVO>(notification.getBody())
Case 1 does not throw error, if you have such a behaviour, there must be an error in notification.getBody() method.
EDIT: #divillysausages made a clever comment about case 2 actually creating an object of another type. This is not the case here. This is what mostly happens for native types with one exception: the Array class. Some of the native classes have top level converting functions. Refer to adobe livedocs for the complete list of them. A Vector can be instantiated this way by passing an Array of appropriate types to the Vector() function.
Something else must happen to the Vector within your class because it's valid to cast a vector to Object and then back to Vector. This simple test shows it:
var v:Vector.<int> = new Vector.<int>();
v.push(1);
v.push(2);
var o:Object = v as Object;
var v2:Vector.<int> = o as Vector.<int>;
trace(v2[0]); // Output "1"
trace(v2[1]); // Output "2"
So your problem must be somewhere else.

AS3 - Returning a property of a class rather than the class itself

In ActionScript 3, there are some classes that will represent a value rather than the class itself. It's hard to explain properly what I mean, so take this example:
var str:String = "something";
var mc:MovieClip = new MovieClip();
trace(str); // something
trace(mc); // [object MovieClip]
You'll notice that the first trace outputs a value, rather than [object String]. Ontop of this, I can still make use of methods of String, like this:
var ar:Array = str.split('s');
Even though in a way you could almost read the above as:
"something".split('s');
I have a class AvLevelData that has some methods that deal with level data (which is essentially a String). At the moment there is a property data:String which represents the core level data.
The question I have is - can I replicate the behaviour of String in that when I trace or assign an instance of AvLevelData, the result is actually the String data.
For example, at the moment I need to go:
var levelData:AvLevelData = new AvLevelData();
trace(levelData.data);
To get the data. I instead want to be able to simply do the following:
var levelData:AvLevelData = new AvLevelData();
trace(levelData); // some level data string
Is this possible?
If you wan't your object to trace out your own fabricated string then you must implement a toString() function on your AvLevelData class.
In your example above, the MovieClip trace outputs: [Object MovieClip]; this comes from the default toString() implementation for Object (found on Object.prototype) . Note, you cannot override toString() as it only exists on the prototype of Object (remnants of the AS2/Javascript world), all you need to do is provide your own implementation with the same name. For instance:
public function toString():String {
return "MyCustomObjectString";
}
Some of the most basic types - String, int, Number, uint, Boolean, to name a few - are not classes / objects per se, they are primitives. In some languages there is a wrapper class available for some of these so they can be treated like objects, though Flash doesn't do this so much from my experience.
Probably the best way to answer your question is to make a toString() method for your AvLevelData class:
public function toString():String {
return data;
}
Any time you treat a class as a string (such as by putting it in trace()), flash (and many other languages) try to call toString() on the object. Typically this results in a string that's not helpful. But if you define your own toString() method, you can control what string gets output.
Another option is to simply do:
trace(AvLevelData.data);
Since that variable is a string, it should trace just fine.

Creating a "true" HashMap implementation with Object Equality in ActionScript 3

I've been spending some of my spare time working a set of collections for ActionScript 3 but I've hit a pretty serious roadblock thanks for the way ActionScript 3 handles equality checks inside Dictionary Objects.
When you compare a key in a dictionary, ActionScript uses the === operator to perform the comparison, this has a bit of a nasty side effect whereby only references to the same instance will resolve true and not objects of equality. Here's what I mean:
const jonny1 : Person = new Person("jonny", 26);
const jonny2 : Person = new Person("jonny", 26);
const table : Dictionary = new Dictionary();
table[jonny1] = "That's me";
trace(table[jonny1]) // traces: "That's me"
trace(table[jonny2]) // traces: undefined.
The way I am attempting to combat this is to provide an Equalizer interface which looks like this:
public interface Equalizer
{
function equals(object : Object) : Boolean;
}
This allows to to perform an instanceOf-esq. check whenever I need to perform an equality operation inside my collections (falling back on the === operator when the object doesn't implement Equalizer); however, this doesn't get around the fact that my underlying datastructure (the Dictionary Object) has no knowledge of this.
The way I am currently working around the issue is by iterating through all the keys in the dictionary and performing the equality check whenever I perform a containsKey() or get() operation - however, this pretty much defeats the entire point of a hashmap (cheap lookup operations).
If I am unable to continue using a Dictionary instance as the backing for map, how would I go about creating the hashes for unique object instances passed in as keys so I can still maintain equality?
How about you compute a hash code for your objects when you insert them, and then look them up by the hash code in your backing dictionary? The hashcode should compare === just fine. Of course, that would require you to have a Hashable interface for your object types instead of your Equalizer interface, so it isn't much less work than you are already doing, but you do get the cheap lookups.
How about rather doing this:
public interface Hashable {
function hash():String;
}
personally, I ask myself, why you want to do this ... hashing objects to obtain keys makes little sense if they are mutable ...
also, you might consider using a different approach, as for example this factory:
package {
public class Person {
/**
* don't use this!
* #private
*/
public function Person(name:String, age:int) {
if (!instantiationAllowed)
throw new Error("use Person.getPerson instead of constructor");
//...
}
private static var instantiationAllowed:Boolean = false;
private static var map:Object = {};
private static function create(name:String, age:int):Person {
instantiationAllowed = true;
var ret:Person = new Person(name, age);
instantiationAllowed = false;
}
public static function getPerson(name:String, age:int):Person {
var ageMap:Array = map[name];
if (ageMap == null) {
map[name] = ageMap = [];
return ageMap[age] = Person.create(name, age);
}
if (ageMap.hasOwnProperty(age))
return ageMap[age];
return ageMap[age] = Person.create(name, age);
}
}
}
it ensures, there's only one person with a given name and age (if that makes any sense) ...
Old thread I know, but still worth posting.
const jonny1 : Person = new Person("jonny", 26); const jonny2 : Person = new Person("jonny", 26);
is creating two completely different objects that will not compare using ==, guess I don't see why it's any more of a road block because of as3
The problem with AS3/JavaScript/EcmaScript is not that they create two different, equivalent objects.
The problem is that they cannot equate those two equivalent objects--only identity works, since there is no equals or hashCode methods that can be overriden with class-specific comparison logic.
For Map implementations such as dynamic Object or Dictionary, this means that you have to either use Strings or references as keys: you cannot recover objects from a map using different but equivalent objects.
To work around that problem, people either resort to strict toString implementations (for Object maps) which is undesirable, or to instance control for Dictionaries, as in #back2dos example, which introduces different problems (Also, note that #back2dos solution does not really guarantee unique Person instances since there is a time window during which asynchronous threads will be allowed to instantiate new Persons).
#A.Levy's solution is good except that in general, hashCodes are not strictly required to issue unique values (they are meant to map entries to buckets allowing for fast lookups, wherein fine-grained differentiation is done through equals method).
You need both a hashCode and an equals method, e.g.
public interface IEquable
{
function equals(object : Object) : Boolean;
function hash():String;
}
In any programming language,
const jonny1 : Person = new Person("jonny", 26);
const jonny2 : Person = new Person("jonny", 26);
is creating two completely different objects that will not compare using ==, guess I don't see why it's any more of a road block because of as3