Whenever I try to extend the Object prototype, I get an error:
Error #1056: Cannot create property my_extension on mx.core.UIComponentDescriptor.
I searched around, and found these:
Flash AS3: ReferenceError: Error #1056: Cannot create property
ReferenceError: Error #1056 - instance name Error
I'm not using a visual IDE for designing a stage, but MXML and AS3 files, so I'm not sure what to make of this error.
My code:
Object.prototype.keys = function(): Array {
var keys: Array = [];
for (var key: * in this) {
keys.push(key);
}
return keys;
}
Object.prototype.values = function(): Array {
var values: Array = [];
for each (var value: * in this) {
values.push(value);
}
return values;
}
Using prototype to extend a class seems very ActionScript 1 or 2.
In AS3, you may be able to prototype if the class is dynamic.
There are downsides to prototype:
Only dynamic classes can be extended, one can not add methods to Math for example.
Calls to methods stored in the prototype take longer to execute.
Since methods are added at run-time, editors can not show them with code hinting or use the correct syntax highlighting.
Since all classes extend object, it is not necessary to explicitly declare Object as a base; however, you could define an AbstractObject class to be extended:
package
{
public dynamic class AbstractObject extends Object
{
public function AbstractObject()
{
super();
}
public function get keys():Array
{
var keys:Array = [];
for (var key:* in this)
{
keys.push(key);
}
return keys;
}
public function get values():Array
{
var values:Array = [];
for each (var value:* in this)
{
values.push(value);
}
return values;
}
}
}
Subtype AbstractObject for your classes.
Related
I have been working on an AS3 project for some while and I think that I've hit a wall. My project requires a series of elements to be arranged in a Circular List, so I copied myself in a Circular List I had done before in C#.
Problem is, that one was heavily dependant on the usage of Generics. Now I don't have those.
Here are the codes. The T variable type represents the generics that I wish existed.
Node.as:
package
{
public class Node
{
var nodeContent:T;
var nextNode:Node;
function Node(nodeElement:T)
{
this.nodeContent = nodeElement;
}
}
}
CircularList.as:
package
{
public class CircularList
{
var head:Node;
var tail:Node;
var listLength:int;
function CircularList()
{
this.head = null;
this.tail = null;
this.listLength = 0;
}
function Add(addition:T)
{
adding:Node = new Node(addition);
if(this.head == null)
{
this.head = adding;
this.tail = adding;
head.nextNode = tail;
tail.nextNode = head;
}
else
{
tail.nextNode = adding;
tail = adding;
tail.nextNode = head;
}
listLength++;
}
function Find(requested:T):Node
{
var finder:Node = null;
var searching = head;
var i:int;
while(i <= listLength)
{
if(searching.nodeContent == requested)
{
finder = searching;
}
searching = searchig.nextNode;
i++;
}
return finder;
}
}
}
Is there a way to make this thing work without the generics?
EDIT: The real problem with this is that I want the NodeContent in the Node class to be an object. I want to make a list of people sitting on a circular table, basically, but I would like to have a code that I can reuse, rather than something specifically made for this problem
From the comments it seems like your best option here would be to use an interface.
Instead of using a type have all classes T implement an interface like INode. In this interface you can define all the functionality that your type T requires and implement it as needed in each of your implementing classes. This way you can change your function signatures to take type INode instead of Class or * and have a common set of methods that these functions can act upon.
function Add(addition:INode){
//add logic on INode
}
function Find(requested:INode):Node{
//find logic on INode
}
edit: a bit of info about interfaces,
http://active.tutsplus.com/tutorials/actionscript/as3-101-oop-introduction-to-interfaces/
say we have two Classes, A, B and each of these classes have a similar method, doTrace, that needs to be implemented differently. We can define an interface, implement it in both of these classes and pass that type into any method looking to call doTrace
Start with the interface called ITraceable,
public interface ITraceable{
function doTrace():void //all methods defined in interfaces are seen as public
}
Now our two Classes, A and B
public class A implements ITraceable { //implementing our interface, when we do this we need to define all methods in ITraceable
public function doTrace():void{
trace("I am A");
}
}
Do a similar thing for B
public class B implements ITraceable {
public function doTrace():void{
trace("I am B");
}
}
Now in some outside class we want to use this
public function letsTrace():void{
doTheTrace(new A()) //I am A
doTheTrace(new B()) //I am B
}
public function doTheTrace(object:ITraceable):void { //now we can pass both A and B into this function
object.doTrace(); //since ITraceable requires all objects that implement it have this method we can guarantee it will be here
}
Hope this helps you through your application
So according to this discussion there is no constant time method for getting the size of a Dictionary object.
I'm trying to implement a wrapper which adds this functionality. Nothing fancy - just a 'numPairs' property and overridden methods to keep it updated. The problem lies in that the [] operator is used to add key/value pairs, rather than a named method, so I don't know how to override this to keep my counter updated. I could just do something like...
public function addPair(key:*, val:*):void {
this[key] = val;
numPairs++;
}
...but it'd be really nice if I could keep the bracket notation. Does anyone know of a solution?
If you want to keep the bracket notation you can still use the Proxy class for that, wrapping a real dictionary.
Here an implementation using the Proxy class, but here i didn't use a weak dictionary because it can be tricky as the 'key' can be garbaged collected and you will not be aware of that. Of course performance operation (adding, removing, ...) will also be lower than the real dictionary.
here the live test : http://wonderfl.net/c/dstz
import flash.utils.Dictionary;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class MyDict extends Proxy {
private var _size:int = 0;
private var _dict:Dictionary = new Dictionary();
public function get size():int {
return _size;
}
flash_proxy override function getProperty(name:*):* {
return _dict[name];
}
flash_proxy override function setProperty(name:*, value:*):void {
if (!_dict.hasOwnProperty(name))
_size ++;
_dict[name] = value;
}
flash_proxy override function deleteProperty(name:*):Boolean {
if (_dict.hasOwnProperty(name)) {
_size --;
delete _dict[name];
return true;
}
return false;
}
}
var dict:MyDict = new MyDict();
dict[1] = 2;
dict["foo"] = "bar";
trace(dict.size, dict[1], dict["foo"]);
delete dict[1];
trace(dict.size, dict[1], dict["foo"]);
I understand you like that you want to keep actual pair count, and you should also check for undefined/null value passed that would indicate a removal of pair. So, you first check if there is a key in "this", then assign value.
public function addPair(key:String, val:*):void {
if (this[key]) {
// pair exists, updating
this[key]=val;
if (!val) numPairs--;
} else {
// pair does not exist, adding
if (val) {
this[key]=val;
numPairs++;
}
}
}
"This" is incapable of finding the appropriate variable "array1" despite it clearly being declared within the function. But if I declare the variables outside of the function it works. How can I have the variables inside the function but keep it working?
package
{
public class main extends MovieClip
{
//If I declared the variables here it would work.
public function main():void
{
var array1:Array = [1,2];
var array2:Array = [3,4];
trace(this["array"+1][1]); //returns reference error 1069
}
}
}
Am I stuck with declaring the variables outside of the function?
And no, multidimensional arrays won't work for what I need it for. Though it looks like it would solve everything within the code snippet provided huh?
My intentions is to pass arrays through a class to be used and change which array bunch I use. If I used multidimensional arrays, it would be inefficient due to the amount of copying that would occur.
For this[] to access properties, those properties must belong to this. In your sample, the properties belong to the function in which they were defined and are inaccessible outside of that scope.
So firstly; yes, for your code to work you will of course need to define properties in the class level scope.
But more importantly I'd look closely at what you're trying to do and determine whether it's a good approach - my bet is that it's not. It seems like you may want to consider an isolated class that deals with all the data you want to store.
Your error is because you mis-scoped the variables.
The "this" keyword means you are trying to target a variable on the specific instance of the class.
You have scoped the variables locally to the function.
You need to move them to the class declaration area for "this" to work.
package
{
public class main extends MovieClip
{
public var array1:Array = [1,2];
public var array2:Array = [3,4];
public function main():void
{
trace(this["array"+1][1]); //returns reference error 1069
}
}
}
// now if you meant to scope them locally to the function then you can not use "this"
// you have to assign them to an object or an array
package
{
public class main extends MovieClip
{
public function main():void
{
var obj:Object = {}
obj['array1'] = new Array( [1,2] )
obj['array2'] = new Array( [3,4] )
trace(obj["array"+1][1]);
}
}
}
This is untested code but it should put you on the right track.
Depending on what you are trying to accomplish exactly, you can also consider passing an array as an argument to a function, something like:
function main():void
{
var array1:Array = [1,2];
var array2:Array = [3,4];
doSomethingWithArray(array2);
}
main();
function doSomethingWithArray(arr:Array):void
{
trace(arr[1]); //Traces the value of array2[1], which = 4
}
Also consider having a var currentArray:Array that you can set as any array you'd like and refer to it as needed, if applicable.
public class main extends MovieClip
{
...
public var currentArray:Array;
function main():void
{
var array1:Array = [1,2];
currentArray = array1;
doSomethingithCurrentArray();
var array2:Array = [3,4];
currentArray = array2;
doSomethingithCurrentArray();
currentArray = null;
}
public function doSomethingithCurrentArray():void {
if(currentArray != null){
trace(currentArray);
}
}
}
So I've never worked with Vectors in Flash before and I wanted to write a little test application with a Vector using a custom object. But I'm getting: error 1000: Ambiguous reference to Vector when I try launching the application. I can't figure out for the life of me what's wrong. I tried not using a custom object and just instantiating a string Vector from an online tutorial and I'm getting the same thing.
Here's what I got:
package
{
import TestPlayer; // The custom player class
import flash.display.MovieClip;
public class Vector extends MovieClip
{
private var array:Array = new Array();
private var vector:Vector.<TestPlayer>;
public function Vector()
{
array[0] = [0, 0, "Bob", false];
array[1] = [1, 0, "Frank", true];
array[2] = [2, 1, "Sarah", true];
Load();
}
private function Load():void
{
var aPlayer:Player = null;
vector = new Vector.<TestPlayer>();
try
{
var numRows:int = array.length;
for (var i = 0; i < numRows; i++)
{
aPlayer = new Player();
aPlayer.playerID = array[i][0];
aPlayer.playerName = array[i][1];
aPlayer.playerTypeID = array[i][2];
aPlayer.hasProgress = array[i][3];
vector.push(aPlayer);
}
}
catch (error:Error) { }
}
}
The custom player class looks like this:
package
{
public class TestPlayer
{
private var _playerID:int;
private var _playerName:String = "";
public function get playerID():int
{
return _playerID;
}
public function set playerID(value:int):void
{
_playerID = value;
}
public function get playerName():String
{
return _playerName;
}
public function set playerName(value:String):void
{
_playerName = value;
}
[...]
}
}
I don't know if it matters, but I'm working in Flash CS5, and I have a blank FLA that imports the class. No other errors so far. Hope you can help. Let me know if you need anymore info, thanks.
The ambiguous reference is because you've got a naming collision. The class you've written is named "Vector", which it can't distinguish from the top-level class Vector. The fix is simple, avoid naming your classes the same as a pre-existing class.
If both classes belong to separate namespaces, you can reuse class names, as long as you use thier fully-qualified name whenever you call the class.
Assume you have a class:
package foo.bar
{
class MovieClip
...
}
You could instantiate both types of MovieClips as follows:
flashMovieClip = new flash.display.MovieClip();
myMovieClip = new foo.bar.MovieClip();
Unfortunately, both your Vector and the flash Vector exist in the top-level namespace, so (AFAIK) there's no way of removing the ambiguity without renaming your class. For simplicity sake, avoid naming collisions and you should be golden.
If an AS3 method returns a reference to a complex type, is there any way to make that 'readonly', like how you can have const member functions in C++? An architecture I want to use calls for a class building itself from a passed template object... and really the template object should not be modifiable. I'm currently forced to add call-back enumerators and/or lots of extra accessor methods.
Flex has an ObjectUtil.clone() method that will make a deep copy. The copy will still by modifiable, but since it's a copy, the changes won't propagate back to the original.
The method is no complicated so if you're not using Flex, just add this to a util class:
public static function copy(value:Object):Object
{
var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;
}
There is no way to do that in AS3, there is Sam's way of doing it, but it still requires copying that object before you return it, depending on the complexity of that object, it can impact the performance.
Immutable interfaces are a near-equivillant to const-correctness. Here's an example:
interface CPoint {
function get x():Number;
function get y():Number;
}
class Point implements CPoint {
private var _x:Number;
private var _y:Number;
public function get x():Number { return _x; }
public function get y():Number { return _y; }
public function set x(val:Number) { _x = val; }
public function set y(val:Number) { _y = val; }
public function normalize():void {
var length:Number = Math.sqrt(_x*_x + _y*_y);
_x /= length;
_y /= length;
}
public function Point(x:Number, y:Number) {
_x = x; _y = y;
}
}
If you return a Point as a CPoint reference, then its fields cannot be altered. You can do an explicit cast to a Point from a CPoint to force access, but you can do the same thing with const casting in C++.
Unfortunately, AS3 doesn't support covariance like it should, so things get unnecessarily difficult for const sub-objects. For example, if you had a Line class that was made up of two points, you might want to say line.start.x = 47; if you have full access to the line, but allow reading of line.start.x through an immutable interface. You could do this if there was covariance, but instead you'll need to add separate get properties for mutable and immutable properties. So, you'd end up instead with line.cstart.x for reads from a CLine. Something like this:
interface CLine {
function get cstart():CPoint;
function get cend():CPoint;
}
class Line implements CLine {
private var _end:Point;
private var _start:Point;
public function get cend():CPoint { return _end; }
public function get cstart():CPoint { return _start; }
public function get end():Point { return _end; }
public function get start():Point { return _start; }
public function Line(x1:Number, y1:Number, x2:Number, y2:Number) {
_start = new Point(x1, y1);
_end = new Point(x2, y2);
}
}
I would create a flash.utils.proxy object. You could create a proxy object that has read only implementation of a child that is passed in.
Here is the documentation for creating a proxy object. http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/Proxy.html
Note: Proxy is pretty damn slow, since you'll be bypassing native object checking, and replacing it with a function call -- which when using a lot will be slow. I would do some simple performance testing first.
note: This is pseudo-code.
use namespace flash_proxy;
dynamic class ReadOnly extends flash.utils.Proxy {
private var target:Object;
public function ReadOnly(target:Object) {
this.target = target;
}
flash_proxy function getProperty(name:*):*
return target[name];
}
flash_proxy function setProperty(name:*, value:*):void
// throw an error or do nothing
}
}
You could then do:
var readOnly:ReadOnly = new ReadOnly(stage.loaderInfo.parameters);
readOnly.someparameter = 'newvalue';
trace(readOnly.someparameter); // should be old value