Typy danych

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]".