Typ danych
definiuje zbiór wartości. Na przykład typ danych Boolean to zbiór zawierający dokładnie dwie wartości:
true
i
false
. Obok typu danych Boolean w języku ActionScript 3.0 istnieje kilka częściej używanych typów danych, np. String, Number i Array. Programista może definiować własne typy danych, definiując własny zbiór wartości przy użyciu klas lub interfejsów. W języku ActionScript 3.0 wszystkie wartości, zarówno pierwotne, jak i złożone
,
są obiektami.
Wartość pierwotna
to wartość należąca do jednego z następujących typów danych: Boolean, int, Number, String lub uint. Przetwarzanie wartości pierwotnych odbywa się zwykle szybciej niż przetwarzanie wartości złożonych, ponieważ w środowisku ActionScript wartości pierwotne są przechowywane w specjalny sposób umożliwiający optymalne wykorzystanie pamięci i przyspieszenie operacji.
Uwaga:
Z myślą o czytelnikach zainteresowanych szczegółami technicznymi wyjaśniamy, że w środowisku ActionScript wartości pierwotne są wewnętrznie przechowywane jako obiekty niezmienne. Fakt, że są przechowywane jako obiekty niezmienne, oznacza, że przekazywanie ich przez referencję (odwołanie) jest faktycznie tożsame z przekazywaniem przez wartość. Ogranicza to zużycie pamięci i przyspiesza wykonywanie kodu, ponieważ odwołania zajmują zwykle znacznie mniej miejsca niż same wartości.
Wartość złożona
to każda wartość niebędąca wartością pierwotną. Do typów danych, które definiują zbiór wartości złożonych, należą Array, Date, Error, Function, RegExp, XML i XMLList.
W wielu językach programowania istnieje rozróżnienie między wartościami pierwotnymi a ich obiektami opakowaniowymi (ang. wrapper). Na przykład w języku Java istnieje typ wartości pierwotnych int oraz opakowująca je klasa java.lang.Integer. Wartości pierwotne w języku Java nie są obiektami, natomiast ich opakowania — już tak. Dlatego do pewnych operacji lepiej nadają się wartości pierwotne, a do innych lepiej jest zastosować odpowiednie obiekty opakowaniowe. W języku ActionScript 3.0 wartości pierwotne i ich obiekty opakowaniowe są, ze względów praktycznych, nierozróżnialne. Wszystkie wartości, nawet pierwotne, są obiektami. W czasie wykonywania te typy pierwotne są traktowane w specjalny sposób, dzięki czemu zachowują się jak obiekty, ale z ich tworzeniem nie jest związany narzut charakterystyczny dla klasycznych obiektów. Oznacza to, że następujące dwa wiersze kodu są równoważne:
var someInt:int = 3;
var someInt:int = new int(3);
Wszystkie wymienione powyżej pierwotne i złożone typy danych są zdefiniowane przez klasy podstawowe języka ActionScript 3.0. Klasy podstawowe umożliwiają tworzenie obiektów przy użyciu wartości literalnych, bez stosowania operatora
new
. Na przykład możliwe jest utworzenie tablicy przy użyciu wartości literalnej lub konstruktora klasy Array, tak jak przedstawiono to poniżej:
var someArray:Array = [1, 2, 3]; // literal value
var someArray:Array = new Array(1,2,3); // Array constructor
Kontrola typów
Kontrola typów może być realizowana w czasie kompilacji lub w czasie wykonywania. W językach z typami statycznymi, takich jak C++ i Java, kontrola typów odbywa się zawsze w czasie kompilacji. W językach z typami dynamicznymi, takich jak Smalltalk i Python, kontrola typów odbywa się w czasie wykonywania. Ponieważ w języku ActionScript 3.0 typy mają charakter dynamiczny, kontrola odbywa się w czasie wykonywania, jednak istnieje również możliwość kontroli typów w trakcie kompilacji w specjalnym trybie, nazywanym
trybem ścisłym
. W trybie ścisłym kontrola typów odbywa się zarówno w czasie kompilacji, jak i w czasie wykonywania, natomiast w trybie standardowym — jedynie w czasie wykonywania.
Języki z typami dynamicznymi oferują wyjątkową elastyczność w budowie struktury kodu, jednak za cenę potencjalnego ujawnienia błędów niezgodności typów w trakcie wykonywania programu. W językach z typami statycznymi błędy niezgodności typów są zgłaszane w trakcie kompilacji, jednak ich niedogodnością jest konieczność znajomości typów już na etapie kompilacji.
Kontrola typów w czasie kompilacji.
Kontrola typów w czasie kompilacji jest często preferowanym rozwiązaniem w dużych projektach, w których elastyczność w użyciu typów danych jest zazwyczaj mniej istotna niż możliwe wczesne wykrywanie wszelkich błędów. Dlatego domyślnie kompilator języka ActionScript w programach Flash Professional i Flash Builder działa w trybie ścisłym.
Adobe Flash Builder
W programie Flash Builder można wyłączyć tryb ścisły za pośrednictwem ustawień kompilatora ActionScript w oknie dialogowym właściwości projektu.
Aby możliwa była kontrola typów w trakcie wykonywania, kompilator musi dysponować informacjami o typach danych zmiennych lub wyrażeń w kodzie. Aby jawnie zadeklarować typ danych zmiennej, należy za jej nazwą umieścić operator dwukropka (
:
), a następnie nazwę typu. Aby skojarzyć typ danych z parametrem, należy użyć dwukropka wraz z następującą po nim nazwą typu. Na przykład w poniższym kodzie określany jest typ parametru
xParam
oraz deklarowana jest zmienna
myParam
o jawnie określonym typie danych:
function runtimeTest(xParam:String)
{
trace(xParam);
}
var myParam:String = "hello";
runtimeTest(myParam);
W trybie ścisłym kompilator języka ActionScript zgłasza niezgodności typów jako błędy kompilatora. Na przykład w poniższym kodzie zadeklarowany jest parametr funkcji
xParam
typu Object, a następnie podejmowana jest próba przypisania temu parametrowi wartości typu String i Number. W trybie ścisłym spowoduje to zgłoszenie błędu przez kompilator.
function dynamicTest(xParam:Object)
{
if (xParam is String)
{
var myStr:String = xParam; // compiler error in strict mode
trace("String: " + myStr);
}
else if (xParam is Number)
{
var myNum:Number = xParam; // compiler error in strict mode
trace("Number: " + myNum);
}
}
Jednak nawet w trybie ścisłym możliwe jest selektywne wyłączenie kontrolowania zgodności typów w czasie wykonywania. Należy w tym celu pominąć wskazanie typu po prawej stronie instrukcji przypisania. Zmienną lub wyrażenie można oznaczyć jako niemającą/niemające typu, pomijając wskazanie typu lub używając gwiazdki (
*
) jako nazwy typu. Przykładowo, jeśli parametr
xParam
z poprzedniego przykładu zostanie zmodyfikowany tak, aby nie był wskazywany jego typ, poniższy kod zostanie skompilowany bez błędów nawet w trybie ścisłym:
function dynamicTest(xParam)
{
if (xParam is String)
{
var myStr:String = xParam;
trace("String: " + myStr);
}
else if (xParam is Number)
{
var myNum:Number = xParam;
trace("Number: " + myNum);
}
}
dynamicTest(100)
dynamicTest("one hundred");
Kontrola typów w czasie wykonywania
W środowisku ActionScript 3.0 zgodność typów jest kontrolowana w czasie wykonywania niezależnie od tego, czy kompilacja odbywała się w trybie ścisłym, czy standardowym. Rozważmy sytuację, w której wartość 3 zostanie przekazana jako argument do funkcji, która oczekuje tablicy. W trybie ścisłym kompilator wygeneruje błąd, ponieważ wartość 3 jest niezgodna z typem danych Array. W wypadku wyłączenia trybu ścisłego i kompilacji w trybie standardowym kompilator nie zgłosi niezgodności typów, ale kontrola typów w trakcie wykonywania kodu spowoduje zgłoszenie błędu.
Poniższy przykład ilustruje funkcję o nazwie
typeTest()
, która oczekuje argumentu typu Array, a do której przekazano wartość 3. W trybie standardowym spowoduje to błąd czasu wykonywania, ponieważ wartość 3 nie należy do zadeklarowanego typu danych parametru (Array).
function typeTest(xParam:Array)
{
trace(xParam);
}
var myNum:Number = 3;
typeTest(myNum);
// run-time error in ActionScript 3.0 standard mode
Mogą także wystąpić sytuacje, w których błąd w czasie wykonywania zostanie zgłoszony mimo przeprowadzenia kompilacji w trybie ścisłym. Jest to możliwe, jeśli kompilacja odbywa się w trybie ścisłym, ale zrezygnowano kontrolę typów w czasie kompilacji, ponieważ użyto zmiennej bez typu. Użycie zmiennej bez typu nie eliminuje kontroli typów, a jedynie odracza ją do czasu wykonywania. Na przykład, jeśli zmienna
myNum
z poprzedniego przykładu nie ma zadeklarowanego typu danych, kompilator nie może wykryć niezgodności typów, ale kod zgłosi błąd w czasie wykonywania, ponieważ porówna bieżącą wartość
myNum
, która w wyniku przypisania wynosi 3, z typem parametru
xParam
, czyli Array.
function typeTest(xParam:Array)
{
trace(xParam);
}
var myNum = 3;
typeTest(myNum);
// run-time error in ActionScript 3.0
Kontrola typów dopiero w trakcie wykonywania pozwala także na bardziej elastyczne korzystanie z mechanizmów dziedziczenia. Dzięki odroczeniu kontroli typów do momentu wykonania kodu tryb standardowy umożliwia odwoływanie się do właściwości podklasy nawet jeśli dokonano
rzutowania rozszerzającego
(ang. upcast). Rzutowanie rozszerzające ma miejsce, gdy instancja zostaje zadeklarowana przy użyciu klasy bazowej, ale jest tworzona przy użyciu podklasy. Możemy na przykład utworzyć klasę ClassBase, którą można rozszerzyć (klas z atrybutem
final
nie można rozszerzać):
class ClassBase
{
}
Następnie możemy utworzyć podklasę klasy ClassBase o nazwie ClassExtender, która będzie miała jedną właściwość o nazwie
someString
:
class ClassExtender extends ClassBase
{
var someString:String;
}
Używając obu klas, możemy utworzyć instancję zadeklarowaną przy użyciu typu danych ClassBase, ale utworzoną faktycznie przy użyciu konstruktora klasy ClassExtender. Rzutowanie rozszerzające jest uznawane za operację bezpieczną, ponieważ klasa bazowa nie zawiera żadnych właściwości ani metod nieistniejących w podklasie.
var myClass:ClassBase = new ClassExtender();
Podklasa natomiast zawiera właściwości lub metody, których brak w klasie bazowej. Na przykład klasa ClassExtender zawiera właściwość
someString
, która nie istnieje w klasie ClassBase. W trybie standardowym kompilatora ActionScript 3.0 dozwolone jest odwoływanie się do tej właściwości z instancji
myClass
— nie spowoduje to zgłoszenia błędu w czasie kompilacji:
var myClass:ClassBase = new ClassExtender();
myClass.someString = "hello";
// no error in ActionScript 3.0 standard mode
Operator is
Operator
is
umożliwia przetestowanie, czy zmienna lub wyrażenie należy do określonego typu danych. W poprzednich wersjach języka ActionScript tę samą funkcjonalność realizował operator
instanceof
, ale w języku ActionScript 3.0 operatora
instanceof
nie należy stosować do testowania przynależności do typów danych. Do ręcznego sprawdzania typów należy używać operatora
is
, a nie operatora
instanceof
, ponieważ wyrażenie
x instanceof y
sprawdza tylko, czy w łańcuchu prototypów
x
istnieje
y
(a w języku ActionScript 3.0 łańcuch prototypów nie odzwierciedla w pełni hierarchii dziedziczenia).
Operator
is
analizuje właściwą hierarchię dziedziczenia i może być stosowany nie tylko do sprawdzania, czy obiekt jest instancją konkretnej klasy, lecz także czy obiekt jest instancją klasy implementującej konkretny interfejs. W poniższym przykładzie tworzona jest instancja klasy Sprite o nazwie
mySprite
, po czym operator
is
używany jest do sprawdzania, czy
mySprite
jest instancją klas Sprite i DisplayObject oraz czy implementuje interfejs IEventDispatcher:
var mySprite:Sprite = new Sprite();
trace(mySprite is Sprite); // true
trace(mySprite is DisplayObject);// true
trace(mySprite is IEventDispatcher); // true
Operator
is
sprawdza hierarchię dziedziczenia i prawidłowo informuje, że instancja
mySprite
jest zgodna z klasami Sprite oraz DisplayObject (klasa Sprite jest podklasą klasy DisplayObject). Operator
is
sprawdza także, czy
mySprite
dziedziczy z jakichkolwiek klas, które implementują interfejs IEventDispatcher. Ponieważ klasa Sprite dziedziczy z klasy EventDispatcher, która implementuje interfejs IEventDispatcher, operator
is
prawidłowo informuje, że
mySprite
implementuje ten interfejs.
Poniższy przykład ilustruje te same testy, co poprzednio, ale wykonywane za pomocą operatora
instanceof
, a nie
is
. Operator
instanceof
prawidłowo rozpoznaje
mySprite
jako instancję klasy Sprite lub DisplayObject, ale zwraca
false
, gdy próbujemy sprawdzić, czy
mySprite
implementuje interfejs IEventDispatcher.
trace(mySprite instanceof Sprite); // true
trace(mySprite instanceof DisplayObject);// true
trace(mySprite instanceof IEventDispatcher); // false
Operator as
Operator
as
również umożliwia sprawdzenie, czy wyrażenie należy do określonego typu danych. Jednak w przeciwieństwie do operatora
is
, operator
as
nie zwraca wartości typu Boolean. Operator
as
zwraca wartość wyrażenia zamiast
true
albo wartość
null
zamiast
false
. Poniższy przykład ilustruje wynik użycia operatora
as
zamiast operatora
is
w prostym przypadku sprawdzenia, czy instancja klasy Sprite należy do typów danych DisplayObject, IEventDispatcher i Number.
var mySprite:Sprite = new Sprite();
trace(mySprite as Sprite); // [object Sprite]
trace(mySprite as DisplayObject); // [object Sprite]
trace(mySprite as IEventDispatcher); // [object Sprite]
trace(mySprite as Number); // null
Operand po prawej stronie operatora
as
musi być typem danych. Próba użycia jako prawego operandu wyrażenia innego niż typ danych spowoduje zgłoszenie błędu.
Klasy dynamiczne
Klasa
dynamiczna
definiuje obiekt, który może być modyfikowany w czasie wykonywania poprzez dodawanie lub zmienianie właściwości i metod. Klasa, która nie jest dynamiczna, np. klasa String, jest klasą
zapieczętowaną
. Do klasy zapieczętowanej nie można dodawać właściwości ani metod w czasie wykonywania.
Aby utworzyć klasę dynamiczną, należy w jej deklaracji użyć atrybutu
dynamic
. Na przykład poniższy kod tworzy klasę dynamiczną o nazwie
Protean
:
dynamic class Protean
{
private var privateGreeting:String = "hi";
public var publicGreeting:String = "hello";
function Protean()
{
trace("Protean instance created");
}
}
Jeśli później utworzymy instancję klasy
Protean
, będzie możliwe dodawanie do niej właściwości lub metod poza jej definicją. Na przykład poniższy kod tworzy instancję klasy
Protean
i dodaje do tej instancji właściwości o nazwach
aString
oraz
aNumber
:
var myProtean:Protean = new Protean();
myProtean.aString = "testing";
myProtean.aNumber = 3;
trace(myProtean.aString, myProtean.aNumber); // testing 3
Właściwości dodane do instancji klasy dynamicznej istnieją w czasie wykonywania, dlatego wszelka kontrola typów również odbywa się w czasie wykonywania. Nie jest możliwe wskazanie typu właściwości dodanej w ten sposób.
Możliwe jest także dodanie metody do instancji
myProtean
— należałoby w tym celu zdefiniować funkcję i powiązać ją z właściwością instancji
myProtean
. W poniższym kodzie instrukcja trace została przeniesiona do metody o nazwie
traceProtean()
:
var myProtean:Protean = new Protean();
myProtean.aString = "testing";
myProtean.aNumber = 3;
myProtean.traceProtean = function ()
{
trace(this.aString, this.aNumber);
};
myProtean.traceProtean(); // testing 3
Jednak utworzone w ten sposób metody nie mają dostępu do właściwości ani metod prywatnych klasy Protean. Co więcej, nawet odwołania do publicznych właściwości lub metod klasy
Protean
muszą być kwalifikowane słowem kluczowym
this
albo nazwą klasy. Poniższy przykład ilustruje metodę
traceProtean()
, która próbuje uzyskać dostęp do zmiennych prywatnych i publicznych klasy
Protean
.
myProtean.traceProtean = function ()
{
trace(myProtean.privateGreeting); // undefined
trace(myProtean.publicGreeting); // hello
};
myProtean.traceProtean();
Opisy typów danych
Pierwotnymi typami danych są: Boolean, int, Null, Number, String, uint oraz void. Klasy podstawowe języka ActionScript definiują również następujące złożone typy danych : Object, Array, Date, Error, Function, RegExp, XML i XMLList.
Typ danych Boolean
Typ danych Boolean zawiera dwie wartości:
true
oraz
false
. Zmienne typu Boolean nie mogą przyjmować żadnych innych wartości. Domyślna wartość zadeklarowanej, ale niezainicjowanej zmiennej typu Boolean to
false
.
int, typ danych
Wartości typu danych int są przechowywane wewnętrznie jako 32-bitowe liczby całkowite; typ ten obejmuje zbiór liczb całkowitych od
-2 147 483 648 (-2
31
) do 2 147 483 647 (2
31
- 1) włącznie. We wcześniejszych wersjach języka ActionScript istniał tylko typ danych Number używany zarówno do liczb całkowitych, jak i zmiennopozycyjnych. W języki ActionScript 3.0 programista ma dostęp do niskopoziomowych typów maszynowych reprezentujących 32-bitowe liczby całkowite ze znakiem i bez znaku. Jeśli zmienna nie potrzebuje wartości ułamkowych lub bardzo dużych/małych, użycie typu int zamiast typu Number powinno zapewnić większą szybkość wykonywania programu i bardziej oszczędne wykorzystanie pamięci.
Do reprezentowania wartości całkowitych spoza zakresu typu int należy stosować typ danych Number, który uwzględnia wartości z zakresu od plus do minus 9 007 199 254 740 992 (53-bitowe liczby całkowite). Domyślna wartość zmiennych typu int to 0.
Typ danych Null
Typ danych Null zawiera tylko jedną wartość,
null
. Jest to domyślna wartość danych typu String oraz wszystkich klas definiujących złożone typy danych, w tym klasy Object. Żadne inne typy pierwotne, w tym Boolean, Number, int oraz uint, nie zawierają wartości
null
. W czasie wykonywania wartość
null
jest przekształcana na odpowiednią wartość domyślną, jeśli podjęto próbę przypisania
null
do wartości typu Boolean, Number, int lub uint. Tego typu danych nie można używać we wskazaniach typu.
Typ danych Number
W języku ActionScript 3.0 typ danych Number może reprezentować liczby całkowite ze znakiem, liczby całkowite bez znaku i liczby zmiennopozycyjne. Jednak ze względów wydajnościowych należy używać typu danych Number wyłącznie do reprezentacji liczb całkowitych niemieszczących się w zakresie 32-bitowych typów
int
i
uint
oraz liczb zmiennopozycyjnych. Aby liczba była przechowywana jako zmiennopozycyjna, należy zapisać ją z kropką dziesiętną. Pominięcie kropki dziesiętnej spowoduje, że liczba będzie przechowywana jako wartość całkowita
Dane typu Number są zapisywane w formacie 64-bitowym o podwójnej precyzji zgodnie ze specyfikacją IEEE Standard for Binary Floating-Point Arithmetic (IEEE-754). Standard ten reguluje zasady zapisywania liczb zmiennopozycyjnych w słowach 64-bitowych. Jeden bit określa, czy liczba jest dodatnia, czy ujemna. Jedenaście bitów zajmuje wykładnik dla podstawy 2. Pozostałe 52 bity zawierają
mantysę
(ang.
significand lub mantissa
), czyli liczbę podnoszoną do potęgi określonej przez wykładnik.
Dzięki wykorzystaniu części bitów do przechowywania wykładnika, typ danych Number umożliwia przechowywanie liczb zmiennopozycyjnych znacznie większych niż wówczas, gdyby wszystkie bity były zajęte przez mantysę. Na przykład, gdyby w danych typu Number wszystkie 64 bity były zajęte przez mantysę, możliwe byłoby zapisywanie liczb nie większych niż 2
65
- 1. Wykorzystanie 11 bitów na wykładnik sprawia, że w danych typu Number mantysa może być podniesiona do potęgi 2
1023
.
Maksymalna i minimalna wartość danych typu Number przechowywane są we właściwościach statycznych klasy Number o nazwach
Number.MAX_VALUE
i
Number.MIN_VALUE
.
Number.MAX_VALUE == 1.79769313486231e+308
Number.MIN_VALUE == 4.940656458412467e-324
Ceną, jaką trzeba zapłacić za bardzo szeroki zakres wartości typu Number, jest precyzja reprezentacji liczb. W danych typu Number mantysa jest zapisana na 52 bitach, przez co wartości, których nie da się dokładnie zapisać w tej liczbie bitów (np. ułamek 1/3) są tylko przybliżeniami. Jeśli w aplikacji wymagana jest bezwzględna precyzja zapisu liczb dziesiętnych, konieczne będzie użycie oprogramowania, które implementuje dziesiętną arytmetykę zmiennopozycyjną (a nie omawianą tutaj binarną arytmetykę zmiennopozycyjną).
Wartości całkowite typu Number zapisane są tylko na 52 bitach mantysy. Przy użyciu tych 52 bitów i specjalnego bitu ukrytego można zapisać liczby z zakresu od -9 007 199 254 740 992 (-2
53
) do 9 007 199 254 740 992 (2
53
).
Wartość
NaN
jest używana nie tylko jako wartość domyślna zmiennych typu
Number
, lecz również jako wynik wszelkich operacji, które powinny zwracać liczbę, ale jej nie zwracają Na przykład próba obliczenia pierwiastka kwadratowego z liczby ujemnej da w wyniku
NaN
. Inne specjalne wartości typu Number to
dodatnia nieskończoność
oraz
ujemna nieskończoność
.
Uwaga:
Wynikiem dzielenia przez
0
jest
NaN
tylko wówczas, gdy dzielnik również jest równy
0
. Dzielenie przez
0
daje w wyniku
infinity
(nieskończoność), gdy dzielnik jest dodatni, oraz
-infinity
(-nieskończoność), gdy dzielnik jest ujemny.
Typ danych String
Typ danych String reprezentuje ciąg znaków 16-bitowych. Ciągi znaków są przechowywane wewnętrznie w formacie Unicode UTF-16. Ciągi znaków są wartościami niezmiennymi, podobnie jak w języku oprogramowania Java. Każda operacja na wartości typu String zwraca nową instancję ciągu. Wartość domyślna zmiennej z zadeklarowanym typem String jest
null
. Wartość
null
nie jest tym samym, co pusty łańcuch (
""
). Wartość
null
oznacza, że w zmiennej nie jest zapisana wartość, a pusty łańcuch oznacza, że ma wartość typu String, która nie zawiera znaków.
Typ danych uint
Wartości typu danych uint są przechowywane wewnętrznie jako 32-bitowe liczby całkowite bez znaku; typ ten obejmuje zbiór liczb całkowitych od 0 do 4 294 967 295 (2
32
- 1) włącznie. Typ uint jest przeznaczony do stosowania w okolicznościach, w których wymagane jest użycie nieujemnych liczb całkowitych. Na przykład dane typu uint muszą być używane do zapisu wartości kolorów pikseli, ponieważ dane typu int zawierają bit znaku, który zakłóca interpretację wartości kolorów. Do przechowywania wartości całkowitych większych niż maksymalna wartość typu uint należy stosować typ danych Number, który pozwala na zapis 53-bitowych liczb całkowitych. Domyślna wartość zmiennych typu uint to 0.
Typ danych void
Typ danych void zawiera tylko jedną wartość,
undefined
. We wcześniejszych wersjach języka ActionScript wartość
undefined
była domyślną wartością instancji klasy Object. W języku ActionScript 3.0 domyślna wartość instancji klasy Object to
null
. Próba przypisania wartości
undefined
instancji klasy Object spowoduje przekonwertowanie wartości na
null
. Wartość
undefined
można przypisywać tylko zmiennym bez typu. Zmienne bez typu są to zmienne, w których deklaracji pominięto wskazanie typu lub użyto wskazania typu w postaci gwiazdki (
*
). Słowa
void
można użyć tylko jako wskazania typu wartości zwracanej.
Typ danych Object
Typ danych Object jest zdefiniowany przez klasę Object. Klasa Object pełni rolę klasy bazowej dla wszystkich innych definicji klas w języku ActionScript. Wersja typu danych Object w języku ActionScript 3.0 różni się od poprzednich wersji pod trzema względami. Po pierwsze, typ danych Object nie jest już domyślnym typem danych przypisywanym zmiennym bez wskazanego typu. Po drugie typ danych Object nie zawiera już wartości
undefined
, która była domyślną wartością instancji klasy Object. Po trzecie w języku ActionScript 3.0 wartość domyślna instancji klasy Object to
null
.
W poprzednich wersjach języka ActionScript zmiennej bez wskazanego typu automatycznie przypisywany był typ danych Object. Ta zasada nie obowiązuje w języku ActionScript 3.0, w którym wprowadzono koncepcję zmiennej rzeczywiście pozbawionej typu. Zmienne bez wskazanego typu są teraz uznawane za zmienne bez typu. Aby w sposób jednoznaczny zaznaczyć w kodzie, że zmienna jest celowo pozostawiana bez typu, można użyć wskazania typu w postaci symbolu gwiazdki (
*
), który jest równoważny pominięciu typu. Poniżej przedstawiono dwie równoważne instrukcje. Obie one deklarują zmienną
x
bez typu.
var x
var x:*
Tylko zmienne bez typu mogą przyjmować wartość
undefined
. Próba przypisania wartości
undefined
zmiennej o określonym typie danych spowoduje, że środowisko wykonawcze przekonwertuje wartość
undefined
na wartość domyślną tego typu. W przypadku danych typu Object wartością domyślną jest
null
, co oznacza, że w przy próbie przypisania
undefined
do instancji Object wartość zostanie przekształcona na
null
.
Konwersje typów
O konwersji typu mówimy, gdy wartość jest przekształcana w wartość należącą do innego typu danych. Konwersja typu może być
niejawna
albo
jawna
. Konwersja niejawna, która jest nazywana również
koercją
, jest w niektórych przypadkach przeprowadzana w czasie wykonywania. Na przykład: przypisanie wartości 2 zmiennej typu Boolean powoduje, że wartość 2 zostanie przekonwertowana na wartość typu Boolean
true
, a dopiero potem zostanie przypisana do zmiennej. Konwersja jawna, nazywana także
rzutowaniem
, ma miejsce, gdy kod użytkownika nakazuje kompilatorowi potraktowanie zmiennej jednego typu danych jak gdyby należała ona do innego typu. W przypadku wartości pierwotnych rzutowanie faktycznie konwertuje wartości z jednego typu danych na drugi. Aby rzutować obiekt na inny typ, należy ująć nazwę obiektu w nawiasy i poprzedzić nazwą nowego typu. Na przykład poniższy kod rzutuje wartość Boolean na liczbę całkowitą:
var myBoolean:Boolean = true;
var myINT:int = int(myBoolean);
trace(myINT); // 1
Konwersje niejawne
Konwersje niejawne zachodzą w czasie wykonywania w różnych kontekstach:
-
W instrukcjach przypisania
-
Przy przekazywaniu wartości jako argumentów funkcji
-
Gdy wartości są zwracane z funkcji
-
W wyrażeniach zawierających niektóre operatory, np. operator dodawania
+
)
W przypadku typów zdefiniowanych przez użytkownika konwersje niejawne są realizowane pomyślnie, gdy wartość przeznaczona do konwersji jest instancją klasy docelowej lub klasy pochodnej względem klasy docelowej. W wypadku niepowodzenia konwersji niejawnej zgłaszany jest błąd. Na przykład poniższy kod zawiera dwie konwersje niejawne, z których jedna jest realizowana pomyślnie, a druga nie.
class A {}
class B extends A {}
var objA:A = new A();
var objB:B = new B();
var arr:Array = new Array();
objA = objB; // Conversion succeeds.
objB = arr; // Conversion fails.
Konwersje niejawne typów pierwotnych są realizowane poprzez wywołanie tych samych wewnętrznych algorytmów konwersji, które używane są przy konwersji jawnej.
Konwersje jawne
Użycie konwersji jawnych, czyli rzutowania, bywa przydatne przy kompilacji w trybie ścisłym, ponieważ niekiedy chcemy uniknąć zgłoszenia błędu kompilacji spowodowanego niezgodnością typów. Dotyczy to sytuacji, w których wiemy z góry, że wartości zostaną prawidłowo, w sposób wymuszony, przekonwertowane w trakcie wykonywania. Na przykład podczas pracy z danymi odebranymi z formularza, chcemy skorzystać z wymuszonej konwersji ciągów znaków na wartości liczbowe. Poniższy kod wywoła błąd podczas kompilacji mimo że działałby prawidłowo w trybie standardowym:
var quantityField:String = "3";
var quantity:int = quantityField; // compile time error in strict mode
Jeśli chcemy pozostać przy trybie ścisłym, ale jednocześnie skorzystać z wymuszonej konwersji ciągu znaków na liczbę, możemy użyć konwersji jawnej:
var quantityField:String = "3";
var quantity:int = int(quantityField); // Explicit conversion succeeds.
Rzutowanie na typy int, uint i Number
Dowolny typ danych można rzutować na jeden z trzech typów liczbowych: int, uint i Number. Jeśli liczba nie może zostać przekonwertowana z tego samego powodu, wówczas wartość domyślna 0 zostanie przypisana do danych typu int i uint, a wartość domyślna
NaN
zostanie przypisana dodanych typu Number. Podczas konwersji wartości typu Boolean na liczbę wartość
true
zamieniana jest na 1, a
false
na 0.
var myBoolean:Boolean = true;
var myUINT:uint = uint(myBoolean);
var myINT:int = int(myBoolean);
var myNum:Number = Number(myBoolean);
trace(myUINT, myINT, myNum); // 1 1 1
myBoolean = false;
myUINT = uint(myBoolean);
myINT = int(myBoolean);
myNum = Number(myBoolean);
trace(myUINT, myINT, myNum); // 0 0 0
Wartości typu String zawierające tylko cyfry mogą być pomyślnie przekonwertowane na jeden z typów liczbowych. Konwersja na typy liczbowe jest też możliwa w przypadku ciągów znaków, które mają postać liczb ujemnych lub zapisanych szesnastkowo (na przykład
0x1A
). W procesie konwersji ignorowane są początkowe i końcowe białe spacje w ciągu znaków. Można również rzutować ciągi znaków zawierające zapisy liczb zmiennopozycyjnych, korzystając ze składni
Number()
. W razie obecności kropki dziesiętnej rzutowanie
uint()
oraz
int()
zwraca liczbę całkowitą uzyskaną z obcięcia kropki i znaków następujących po niej. Na przykład następujące ciągi znaków można rzutować na liczby:
trace(uint("5")); // 5
trace(uint("-5")); // 4294967291. It wraps around from MAX_VALUE
trace(uint(" 27 ")); // 27
trace(uint("3.7")); // 3
trace(int("3.7")); // 3
trace(int("0x1A")); // 26
trace(Number("3.7")); // 3.7
Wartości ciągów zawierające znaki nieliczbowe są konwertowane na 0 w przypadku rzutowania
int()
lub
uint()
albo
NaN
w przypadku rzutowania
Number()
. W procesie konwersji ignorowane są początkowe i końcowe białe spacje, ale jeśli ciąg zawiera liczby rozdzielone białymi spacjami, zwracana jest wartość 0 lub
NaN
.
trace(uint("5a")); // 0
trace(uint("ten")); // 0
trace(uint("17 63")); // 0
W języku ActionScript 3.0 funkcja
Number()
nie obsługuje już liczb ósemkowych (o podstawie 8). Przekazanie ciągu znaków zaczynającego się od zera do funkcji
Number()
w języku ActionScript 2.0 spowodowało interpretowanie liczby jako ósemkowej i przekonwertowanie na odpowiednią wartość dziesiętną. Natomiast funkcja
Number()
w języku ActionScript 3.0 ignoruje początkowe zero. Na przykład poniższy kod generuje różne wyniki w zależności od tego, w której wersji kompilatora ActionScript zostanie skompilowany:
trace(Number("044"));
// ActionScript 3.0 44
// ActionScript 2.0 36
Rzutowanie nie jest konieczne w przypadku przypisywania wartości jednego typu liczbowego zmiennej innego typu liczbowego. Nawet w trybie ścisłym typy liczbowe są niejawnie konwertowane na inne typy liczbowe. Oznacza to, że w niektórych przypadkach — w razie przekroczenia zakresu typu docelowego — uzyskane wartości mogą być różne od oczekiwanych. Poniższe przykłady dają się prawidłowo skompilować w trybie ścisłym, mimo że wyniki działania niektórych z nich są nieoczekiwane:
var myUInt:uint = -3; // Assign int/Number value to uint variable
trace(myUInt); // 4294967293
var myNum:Number = sampleUINT; // Assign int/uint value to Number variable
trace(myNum) // 4294967293
var myInt:int = uint.MAX_VALUE + 1; // Assign Number value to uint variable
trace(myInt); // 0
myInt = int.MAX_VALUE + 1; // Assign uint/Number value to int variable
trace(myInt); // -2147483648
W poniższej tabeli zamieszczono podsumowanie wyników rzutowania różnych typów danych na typy Number, int lub uint.
Typ danych lub wartość
|
Wynik konwersji na Number, int lub uint
|
Boolean
|
Wartość
true
jest zamieniana na 1; w przeciwnym razie 0.
|
Date
|
Wewnętrzna reprezentacja obiektu Date, czyli liczba milisekund od północy 1 stycznia 1970 roku UTC.
|
null
|
0
|
Obiekt
|
Jeśli instancja jest równa
null
i zostanie przekonwertowana na typ Number, to wartością jest
NaN
; w przeciwnym razie 0.
|
String
|
Liczba, jeśli łańcuch może zostać przekonwertowany na liczbę; w przeciwnym wypadku
NaN
— w przypadku konwersji na Number, albo 0 — w przypadku konwersji na int lub uint.
|
undefined
|
Przy konwersji na typ Number — wartość
NaN
; przy konwersji na typ int lub uint — wartość 0.
|
Rzutowanie na typ Boolean
Rzutowanie dowolnego liczbowego typu danych (uint, int lub Number) na typ Boolean daje w wyniku
false
, jeśli liczba miała wartość 0, albo
true
w przeciwnym razie. W przypadku typu danych Number konwersja wartości
NaN
również daje w wyniku
false
. Poniższy przykład demonstruje wyniki rzutowania liczb -1, 0 i 1:
var myNum:Number;
for (myNum = -1; myNum<2; myNum++)
{
trace("Boolean(" + myNum +") is " + Boolean(myNum));
}
Wyniki wykonania przykładu wykazują, że spośród trzech liczb tylko wartość 0 jest konwertowana na
false
:
Boolean(-1) is true
Boolean(0) is false
Boolean(1) is true
Rzutowanie wartości String na typ Boolean daje w wyniku
false
, jeśli ciąg jest równy
null
lub jest pusty(
""
). W przeciwnym razie wynikiem jest
true
.
var str1:String; // Uninitialized string is null.
trace(Boolean(str1)); // false
var str2:String = ""; // empty string
trace(Boolean(str2)); // false
var str3:String = " "; // white space only
trace(Boolean(str3)); // true
Rzutowanie instancji klasy Object na typ Boolean daje w wyniku
false
, jeśli instancja jest równa
null
; w przeciwnym razie wynikiem jest
true
:
var myObj:Object; // Uninitialized object is null.
trace(Boolean(myObj)); // false
myObj = new Object(); // instantiate
trace(Boolean(myObj)); // true
W trybie ścisłym zmienne typu Boolean są traktowane w szczególny sposób, ponieważ dane dowolnego typu danych można przypisać do zmiennej typu Boolean bez rzutowania. Niejawna wymuszona konwersja wszystkich typów danych na typ Boolean ma miejsce nawet w trybie ścisłym. Innymi słowy, inaczej niż w przypadku niemal wszystkich innych typów danych, rzutowanie na typ Boolean nie jest konieczne dla uniknięcia zgłaszania błędów w trybie ścisłym. Wszystkie poniższe przykłady dają się prawidłowo skompilować w trybie ścisłym i zachowują się zgodnie z oczekiwaniami w czasie wykonywania:
var myObj:Object = new Object(); // instantiate
var bool:Boolean = myObj;
trace(bool); // true
bool = "random string";
trace(bool); // true
bool = new Array();
trace(bool); // true
bool = NaN;
trace(bool); // false
W poniższej tabeli zamieszczono podsumowanie wyników rzutowania różnych typów danych na typ Boolean.
Typ danych lub wartość
|
Wynik konwersji na typ Boolean
|
String
|
false
dla wartości
null
lub pustego ciągu (
""
);
true
w przeciwnym razie.
|
null
|
false
|
Number, int lub uint
|
false
dla wartości
NaN
lub 0;
true
w przeciwnym razie.
|
Obiekt
|
false
, jeśli instancja jest równa
null
;
true
w przeciwnym razie.
|
Rzutowanie na tym String
Rzutowanie dowolnego typu liczbowego na typ String daje w wyniku ciąg znaków reprezentujący liczbę. Rzutowanie wartości Boolean na typ danych String daje w wyniku ciąg znaków
"true"
dla wartości
true
albo ciąg znaków
"false"
dla wartości
false
.
Rzutowanie instancji klasy Object na typ danych String daje w wyniku ciąg znaków
"null"
, jeśli instancja jest równa
null
. W przeciwnym wypadku rzutowanie klasy Object na typ String daje w wyniku ciąg znaków
"[object Object]"
.
Rzutowanie instancji klasy Array na typ String daje w wyniku ciąg znaków zawierający listę elementów tablicy oddzielanych przecinkami. Na przykład poniższe rzutowanie na typ danych String daje w wyniku jeden ciąg zawierający wszystkie trzy elementy tablicy:
var myArray:Array = ["primary", "secondary", "tertiary"];
trace(String(myArray)); // primary,secondary,tertiary
Rzutowanie instancji klasy Date na typ String daje w wyniku ciąg znaków z zapisem daty zawartej w tej instancji. Poniższy przykład zwraca ciąg znaków zawierający zapis daty pochodzącej z instancji klasy Date (wynik wyświetlany jest dla strefy czasowej PDT):
var myDate:Date = new Date(2005,6,1);
trace(String(myDate)); // Fri Jul 1 00:00:00 GMT-0700 2005
W poniższej tabeli zamieszczono podsumowanie wyników rzutowania różnych typów danych na typ String.
Typ danych lub wartość
|
Wynik konwersji na typ String
|
Tablica
|
Łańcuch złożony z samych elementów tablicowych.
|
Boolean
|
"
true
" albo "
false
"
|
Date
|
Ciąg znaków zawierający zapis zawartości obiektu Date.
|
null
|
"null"
|
Number, int lub uint
|
Ciąg znaków reprezentujący liczbę.
|
Obiekt
|
Jeśli instancja jest równa null — ciąg znaków
"null"
; w przeciwnym razie ciąg znaków
"[object Object]".
|
|
|
|