149 62 8MB
Polish Pages 748 Year 2010
Tytuł oryginału: ActionScript 3.0 Bible Tłumaczenie: Sebastian Rataj ISBN: 978-83-246-8437-3 Copyright © 2008 by Wiley Publishing, Inc., Indianapolis, Indiana. All rights reserved. This translation published under license. Translation copyright © 2010 by Helion S.A. Polish edition copyright © 2010 by Helion S.A. Wiley, the Wiley logo and related trade dress are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its affiliates, in the United States and other countries, and may not be used without written permission. ActionScript is a trademark of Adobe Systems Incorporated. All other trademarks are the property of their respective owners. Wiley Publishing, Inc. is not associated with any product or vendor mentioned in this book. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: [email protected] WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie?acs3bi_ebook Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/acs3bi.zip Printed in Poland. Poleć książkę na Facebook.com
Księgarnia internetowa
Kup w wersji papierowej
Lubię to! » Nasza społeczność
Oceń książkę
Pamięci mojej babci Miriam Horn — Roger Braunstein Tę książkę dedykuję Alicie, która nie spała do późnych godzin, aby nagradzać mnie lodami oraz odcinkami serialu „Zagubieni” aż do ukończenia kolejnych rozdziałów — Mims H. Wright
4
ActionScript 3.0. Biblia
Rzut oka na książkę
5
Rzut oka na książkę Wstęp ............................................................................................................... 27
Część I Rozpoczęcie pracy z ActionScript 3.0 .............................. 45 Rozdział 1. Wprowadzenie do ActionScript 3.0 ............................................... 47 Rozdział 2. Podstawy języka ActionScript 3.0 ................................................. 53 Rozdział 3. Programowanie z wykorzystaniem klas ....................................... 81 Rozdział 4. Praca z metodami i funkcjami ..................................................... 139 Rozdział 5. Walidacja programów .................................................................. 155
Część II Praca z obiektami ActionScript 3.0 ............................... 163 Rozdział 6. Używanie łańcuchów znaków ...................................................... 165 Rozdział 7. Praca z liczbami i funkcjami matematycznymi .......................... 175 Rozdział 8. Używanie tablic ............................................................................ 195 Rozdział 9. Używanie obiektów ...................................................................... 215 Rozdział 10. Praca z XML ................................................................................. 225 Rozdział 11. Praca z wyrażeniami regularnymi ............................................. 251
Część III Praca z listą wyświetlania ............................................ 281 Rozdział 12. Lista wyświetlania programu Flash Player 9 ............................ 283 Rozdział 13. Praca z obiektami DisplayObject w programie Flash CS3 ........ 315 Rozdział 14. Drukowanie ................................................................................. 323 Rozdział 15. Praca z tekstem i czcionkami .................................................... 335
Część IV System obsługi zdarzeń ................................................ 363 Rozdział 16. Działanie zdarzeń ....................................................................... 365 Rozdział 17. Praca ze zdarzeniami myszy i klawiatury ................................. 385 Rozdział 18. Korzystanie z czasomierzy ........................................................ 401
6
ActionScript 3.0. Biblia
Część V Obsługa błędów ............................................................. 411 Rozdział 19. Czym są błędy ............................................................................. 413 Rozdział 20. Korzystanie z debugera AVM2 ................................................... 427 Rozdział 21. Aplikacja tolerancyjna na błędy ................................................ 449
Część VI Praca z danymi zewnętrznymi ..................................... 459 Rozdział 22. Podstawy pracy w sieci ............................................................. 461 Rozdział 23. Komunikacja z technologiami serwerowymi ............................ 479 Rozdział 24. Zapisywanie danych na lokalnym komputerze za pomocą SharedObject ........................................................... 489 Rozdział 25. Zarządzanie wysyłaniem i pobieraniem plików ....................... 507
Część VII Wzbogacanie programów dźwiękiem i filmem .......... 517 Rozdział 26. Praca z dźwiękiem ...................................................................... 519 Rozdział 27. Dodawanie wideo ....................................................................... 535 Rozdział 28. Dostęp do mikrofonów i kamer ................................................ 549
Część VIII Programowanie grafiki i ruchu .................................. 557 Rozdział 29. Stosowanie filtrów do grafik .................................................... 559 Rozdział 30. Programowe rysowanie grafik wektorowych ......................... 581 Rozdział 31. Programowanie animacji ........................................................... 609 Rozdział 32. Transformacje grafiki ................................................................ 631 Rozdział 33. Programowe rysowanie grafik bitmapowych ......................... 643
Część IX Praca z danymi binarnymi ............................................ 679 Rozdział 34. Praca z danymi binarnymi ......................................................... 681
Część X Wdrażanie programu ..................................................... 693 Rozdział 35. Wdrażanie zawartości Flash ...................................................... 695 Rozdział 36. Komunikacja z JavaScript .......................................................... 703 Rozdział 37. Wykorzystanie połączeń lokalnych do komunikacji pomiędzy filmami Flash ............................................................. 709 Skorowidz ...................................................................................................... 715
Spis treści O autorach ....................................................................................................... 23 Podziękowania ................................................................................................ 25 Wstęp ............................................................................................................... 27
Część I Rozpoczęcie pracy z ActionScript 3.0 .............................. 45 Rozdział 1. Wprowadzenie do ActionScript 3.0 ............................................... 47 Czym jest ActionScript? .....................................................................................................................47 Do czego używać ActionScript 3.0? ...................................................................................................48 Co nowego w ActionScript 3.0? .........................................................................................................48 Lista wyświetlania ........................................................................................................................49 Błędy czasu wykonywania ............................................................................................................49 Kontrola typów danych w czasie wykonywania ...........................................................................49 Domknięcia metod ........................................................................................................................50 Wewnętrzny model zdarzeń ..........................................................................................................50 Wyrażenia regularne .....................................................................................................................50 E4X ...............................................................................................................................................50 Podsumowanie ....................................................................................................................................51 Rozdział 2. Podstawy języka ActionScript 3.0 ................................................. 53 Stosowanie zmiennych ........................................................................................................................53 Anatomia deklaracji zmiennej ......................................................................................................54 Stałe w tym zmieniającym się świecie ..........................................................................................54 Stosowanie literałów .....................................................................................................................55 Stosowanie kontroli dostępu ...............................................................................................................55 Zasięg ..................................................................................................................................................56 Typy zasięgu .................................................................................................................................57 Łańcuch zasięgu ...........................................................................................................................59 Praca z zasięgiem ..........................................................................................................................61 Typy danych .......................................................................................................................................62 Deklarowanie typów .....................................................................................................................62 Używanie zmiennych bez typu z typem danych wieloznacznika (*) ............................................63 Praca z operatorami .............................................................................................................................63 Operatory jedno- i dwuargumentowe ...........................................................................................63 Kolejność operatorów ...................................................................................................................63 Powszechnie używane operatory ..................................................................................................64
8
ActionScript 3.0. Biblia Dokonywanie logicznych wyborów za pomocą instrukcji warunkowych ...........................................65 Instrukcja if ...................................................................................................................................65 Testowanie innych porównań .......................................................................................................66 if..else ...........................................................................................................................................68 switch ............................................................................................................................................69 Operator warunkowy ....................................................................................................................70 Powtarzanie operacji za pomocą pętli .................................................................................................71 Użycie pętli for .............................................................................................................................71 Używanie for..in oraz for each..in .................................................................................................73 Używanie while oraz do..while .....................................................................................................74 Używać for czy while ...................................................................................................................75 Używanie break oraz continue ......................................................................................................76 Komentowanie kodu ...........................................................................................................................76 Typy komentarzy ..........................................................................................................................77 Kiedy używać komentarzy ............................................................................................................78 Podsumowanie ....................................................................................................................................78 Rozdział 3. Programowanie z wykorzystaniem klas ....................................... 81 Istota klas ............................................................................................................................................81 Klasy mogą modelować rzeczywisty świat ...................................................................................82 Klasy zawierają dane i operacje ....................................................................................................82 Klasy rozdzielają zadania .............................................................................................................83 Klasy są typami ............................................................................................................................83 Klasy zawierają Twój program .....................................................................................................83 Stosowana terminologia ......................................................................................................................84 Obiekt ...........................................................................................................................................84 Klasa .............................................................................................................................................85 Instancja ........................................................................................................................................85 Typ ................................................................................................................................................85 Enkapsulacja .......................................................................................................................................86 Zasada czarnej skrzynki ................................................................................................................86 Enkapsulacja i polimorfizm ..........................................................................................................87 Pakiety ................................................................................................................................................87 Unikalność klasy ...........................................................................................................................88 Hierarchia .....................................................................................................................................89 Kontrola widoczności ...................................................................................................................89 Kod dozwolony w pakietach .........................................................................................................90 Używanie kodu z pakietów ...........................................................................................................91 Korzystanie z dziedziczenia ................................................................................................................93 Struktura kodu zawierającego dziedziczenie ................................................................................97 Dziedziczenie, typy oraz polimorfizm ..........................................................................................98 Dziedziczenie a kompozycja .......................................................................................................100 Zapobieganie dziedziczeniu ........................................................................................................102 Używanie modyfikatorów dostępu do klas .......................................................................................104 Public i private ............................................................................................................................104 Protected .....................................................................................................................................106 Internal ........................................................................................................................................108 Przestrzenie nazw .......................................................................................................................109 Używanie metod w klasach ...............................................................................................................112 Konstruktory ...............................................................................................................................113
Spis treści
9
Używanie właściwości w klasach .....................................................................................................114 Metody dostępowe ......................................................................................................................114 Unikanie efektów ubocznych ......................................................................................................117 Przesłanianie zachowania .................................................................................................................117 Używanie klasy bazowej ............................................................................................................119 Używanie statycznych metod i właściwości .....................................................................................120 Zmienne statyczne ......................................................................................................................121 Stałe statyczne ............................................................................................................................123 Metody statyczne ........................................................................................................................125 Projektowanie interfejsów .................................................................................................................127 Manipulowanie typami .....................................................................................................................133 Zgodność i koercja typów ...........................................................................................................133 Jawna konwersja typu .................................................................................................................134 Określanie typów ........................................................................................................................136 Tworzenie klas dynamicznych ..........................................................................................................137 Podsumowanie ..................................................................................................................................137 Rozdział 4. Praca z metodami i funkcjami ..................................................... 139 Funkcje .............................................................................................................................................139 Różnice pomiędzy metodami i funkcjami ...................................................................................140 Wywołanie funkcji ......................................................................................................................140 Tworzenie własnych funkcji .............................................................................................................141 Definiowanie funkcji ..................................................................................................................141 Przekazywanie parametrów do funkcji .......................................................................................142 Dostęp do obiektu arguments ......................................................................................................145 Zwracanie wyników ..........................................................................................................................146 Zwracanie wartości za pomocą wyrażenia return .......................................................................147 Definiowanie funkcji za pomocą wyrażeń funkcyjnych ...................................................................149 Dostęp do metod superklasy .............................................................................................................150 Pisanie funkcji rekurencyjnych .........................................................................................................151 Leczenie z rekursivitis ................................................................................................................152 Funkcje jako obiekty .........................................................................................................................153 Function a function .....................................................................................................................153 Metody i właściwości klasy Function .........................................................................................154 Podsumowanie ..................................................................................................................................154 Rozdział 5. Walidacja programów .................................................................. 155 Wprowadzenie do błędów .................................................................................................................155 Błędy kompilacji a błędy czasu wykonywania ...........................................................................156 Ostrzeżenia .................................................................................................................................156 Informacje zwrotne od programów Flash CS3 oraz Flex Builder ...............................................156 Naprawianie błędów .........................................................................................................................159 Powszechne typy błędów ............................................................................................................160 Podsumowanie ..................................................................................................................................162
Część II Praca z obiektami ActionScript 3.0 ............................... 163 Rozdział 6. Używanie łańcuchów znaków ...................................................... 165 Praca z prostymi łańcuchami znaków ...............................................................................................165 Konwertowanie obiektu łańcuchowego na typ prosty ................................................................166 Używanie sekwencji ucieczki .....................................................................................................166
10
ActionScript 3.0. Biblia Konwersja na łańcuchy i z łańcuchów znakowych ...........................................................................167 Użycie toString ...........................................................................................................................167 Rzutowanie na typ String ............................................................................................................168 Konwersja łańcuchów znaków na inne typy ...............................................................................169 Łączenie łańcuchów znaków .............................................................................................................169 Konwersja wielkości znaków w łańcuchu .........................................................................................170 Używanie poszczególnych znaków łańcucha ....................................................................................171 Uzyskanie liczby znaków w łańcuchu ........................................................................................171 Dostęp do poszczególnych znaków ............................................................................................171 Konwersja znaku na kod znaku ..................................................................................................171 Wyszukiwanie w łańcuchu znaków ..................................................................................................172 Wyszukiwanie przez podłańcuch ................................................................................................172 Wyszukiwanie za pomocą wyrażeń regularnych ........................................................................173 Dzielenie łańcuchów .........................................................................................................................173 Podsumowanie ..................................................................................................................................174 Rozdział 7. Praca z liczbami i funkcjami matematycznymi .......................... 175 Typy liczbowe ...................................................................................................................................175 Zbiory liczb .................................................................................................................................175 Reprezentacja liczb .....................................................................................................................176 Cyfrowe reprezentacje liczb .......................................................................................................177 Używanie liczb w ActionScript ........................................................................................................179 Number .......................................................................................................................................179 int ................................................................................................................................................180 uint ..............................................................................................................................................180 Literały .......................................................................................................................................181 Przypadki brzegowe ....................................................................................................................182 Manipulowanie liczbami ...................................................................................................................183 Konwersje liczbowe ....................................................................................................................183 Konwersje łańcuchów znaków ...................................................................................................184 Obliczenia arytmetyczne ...................................................................................................................185 Obliczenia trygonometryczne ...........................................................................................................186 Generowanie losowości ....................................................................................................................187 Manipulowanie wartościami dat i czasów .........................................................................................188 Tworzenie daty ...........................................................................................................................188 Czas epoki ..................................................................................................................................190 Strefy czasowe ............................................................................................................................191 Uzyskiwanie dostępu do daty i jej modyfikacja ..........................................................................191 Arytmetyka dat ...........................................................................................................................192 Czas wykonywania .....................................................................................................................193 Formatowanie daty .....................................................................................................................193 Podsumowanie ..................................................................................................................................194 Rozdział 8. Używanie tablic ............................................................................ 195 Podstawy tablic .................................................................................................................................195 Konstruktor Array .......................................................................................................................196 Tworzenie tablicy za pomocą literału tablicowego .....................................................................197 Odwoływanie się do wartości w tablicy ......................................................................................197 Pobieranie liczby elementów w tablicy .......................................................................................198 Konwersja tablic na łańcuchy znaków ..............................................................................................198
Spis treści
11
Dodawanie i usuwanie elementów z tablicy .....................................................................................199 Dołączenie wartości na końcu tablicy za pomocą concat() .........................................................199 Stosowanie operacji stosu — push() oraz pop() ..........................................................................200 Stosowanie operacji kolejki — shift() oraz unshift() ..................................................................201 Cięcie i łączenie ................................................................................................................................201 Wstawianie i usuwanie wartości za pomocą splice() ..................................................................202 Praca z podzbiorem tablicy przy użyciu slice() ..........................................................................202 Iteracje po elementach tablicy ...........................................................................................................203 Użycie pętli for ...........................................................................................................................203 Użycie metody forEach() ............................................................................................................203 Wyszukiwanie elementów ................................................................................................................204 Zmiana kolejności w tablicy .............................................................................................................204 Odwracanie kolejności tablicy za pomocą reverse() ...................................................................205 Użycie funkcji sortujących .........................................................................................................205 Przeprowadzanie operacji na wszystkich elementach tablicy ...........................................................208 Przetwarzanie warunkowe za pomocą metod every(), some() oraz filter() .................................208 Uzyskiwanie wyników za pomocą metody map() ......................................................................210 Alternatywne typy tablic ...................................................................................................................210 Praca z tablicami asocjacyjnymi .................................................................................................210 Używanie obiektu jako klucza wyszukiwania w słownikach ......................................................211 Używanie tablic wielowymiarowych ..........................................................................................212 Podsumowanie ..................................................................................................................................213 Rozdział 9. Używanie obiektów ...................................................................... 215 Praca z obiektami ..............................................................................................................................215 Klasy dynamiczne .......................................................................................................................215 Tworzenie obiektów ...................................................................................................................216 Dostęp do właściwości obiektu ...................................................................................................217 toString() .....................................................................................................................................217 Używanie instancji klasy Object jako tablicy asocjacyjnej ...............................................................217 Porównywanie tablic, obiektów i słowników .............................................................................218 Testowanie istnienia ...................................................................................................................220 Usuwanie właściwości ................................................................................................................221 Iterowanie ...................................................................................................................................221 Użycie obiektów dla nazwanych argumentów ..................................................................................222 Używanie obiektów jako zagnieżdżonych danych ............................................................................223 XML jako obiekty .......................................................................................................................223 JSON ...........................................................................................................................................223 Podsumowanie ..................................................................................................................................223 Rozdział 10. Praca z XML ................................................................................. 225 Rozpoczęcie pracy z XML w ActionScript .......................................................................................225 Początki E4X ..............................................................................................................................226 Praca z literałami XML ...............................................................................................................226 Krótkie wprowadzenie do operatorów i składni E4X .................................................................227 Klasy XML .................................................................................................................................228 Dostęp do wartości za pomocą E4X .................................................................................................229 Używanie operatora kropki do uzyskiwania dostępu do elementów ...........................................229 Użycie operatora @ do uzyskania dostępu do atrybutów ...........................................................231 Dostęp do tekstu wewnątrz elementu ..........................................................................................231 Operator dostępu do potomków ..................................................................................................232
12
ActionScript 3.0. Biblia Dostęp do przodków ...................................................................................................................233 Iterowanie po dzieciach elementu ...............................................................................................233 Filtrowanie wewnątrz XML ........................................................................................................234 Konstruowanie obiektów XML .........................................................................................................235 Łączenie węzłów XML ...............................................................................................................235 Usuwanie węzłów XML .............................................................................................................238 Duplikowanie obiektu XML .......................................................................................................239 Zamiana wartości w węzłach XML ............................................................................................240 Konwertowanie na łańcuchy znaków i odwrotnie .............................................................................240 Konwertowanie łańcuchów znaków na XML .............................................................................240 Konwertowanie XML na łańcuchy znaków ................................................................................241 Wczytywanie danych XML z zewnętrznych źródeł ..........................................................................243 Gromadzenie metadanych węzłów XML ..........................................................................................244 Używanie przestrzeni nazw ...............................................................................................................245 Praca z przestrzeniami nazw w ActionScript ..............................................................................246 Idąc jeszcze dalej ........................................................................................................................247 Praca z komentarzami i instrukcjami przetwarzania .........................................................................248 Ustawianie opcji dla klasy XML .......................................................................................................249 Podsumowanie ..................................................................................................................................250 Rozdział 11. Praca z wyrażeniami regularnymi ............................................. 251 Wprowadzenie do wyrażeń regularnych ...........................................................................................251 Pisanie wyrażenia regularnego ...................................................................................................252 Stosowanie wyrażeń regularnych ......................................................................................................253 Metody łańcuchowe i metody klasy RegExp ..............................................................................253 Testowanie ..................................................................................................................................253 Lokalizacja ..................................................................................................................................254 Identyfikacja ...............................................................................................................................256 Wyodrębnianie ............................................................................................................................257 Zastępowanie ..............................................................................................................................259 Rozdzielanie ...............................................................................................................................260 Konstruowanie wyrażeń ....................................................................................................................260 Zwykłe znaki ..............................................................................................................................261 Znak kropki .................................................................................................................................261 Sekwencje ucieczki .....................................................................................................................261 Znaczenie metaznaków i metasekwencji ....................................................................................262 Klasy znaków .............................................................................................................................263 Kwantyfikatory ...........................................................................................................................264 Kotwice i granice ........................................................................................................................265 Alternacja ...................................................................................................................................267 Grupy ..........................................................................................................................................267 Flagi wyrażeń regularnych ................................................................................................................268 Flaga global ................................................................................................................................269 Flaga ignoreCase ........................................................................................................................269 Flaga multiline ............................................................................................................................269 Flaga dotall .................................................................................................................................270 Flaga extended ............................................................................................................................271 Konstruowanie zaawansowanych wyrażeń .......................................................................................272 Dopasowywanie zachłanne i leniwe ...........................................................................................272 Odwołania wsteczne ...................................................................................................................273
Spis treści
13
Grupy uprzedzone i nieprzechwycone ........................................................................................274 Grupy nazwane ...........................................................................................................................275 Kwestie międzynarodowe ...........................................................................................................277 Używanie klasy RegExp ...................................................................................................................277 Budowanie dynamicznych wyrażeń za pomocą operacji łańcuchowych ....................................277 Publiczne właściwości RegExp ..................................................................................................278 Podsumowanie ..................................................................................................................................279
Część III Praca z listą wyświetlania ............................................ 281 Rozdział 12. Lista wyświetlania programu Flash Player 9 ............................ 283 Lista wyświetlania ............................................................................................................................283 DisplayObject i DisplayObjectContainer ..........................................................................................289 Czym jest klasa abstrakcyjna? ....................................................................................................290 Pozycja x oraz y obiektu DisplayObject .....................................................................................290 Scena obiektu DisplayObject ......................................................................................................290 Transformacja .............................................................................................................................291 Zmiana rozmiaru obiektu DisplayObject ....................................................................................292 Używanie trybu mieszania ..........................................................................................................292 Obiekt Graphics ................................................................................................................................293 Tworzenie wypełnień ..................................................................................................................294 Rysowanie linii w obiekcie graficznym ......................................................................................294 Rysowanie krzywych w obiekcie graficznym .............................................................................295 Praca ze sceną ...................................................................................................................................295 Użycie zdarzenia stageResize .....................................................................................................295 Ustawianie wyrównywania sceny oraz trybu skalowania ...........................................................296 InteractiveObject i SimpleButton ......................................................................................................296 SimpleButton ..............................................................................................................................297 Obiekty interaktywne dostępne za pomocą klawisza Tab ...........................................................297 Zdarzenia aktywności i klawisza Tab .........................................................................................298 Właściwości myszy ....................................................................................................................299 Zdarzenia otrzymywane przez InteractiveObject ........................................................................300 Shape ................................................................................................................................................301 Tworzenie elementów interfejsu użytkownika za pomocą obiektów Sprite ......................................301 Przeciąganie i upuszczanie .........................................................................................................302 Użycie trybu buttonMode obiektu Sprite ....................................................................................303 Użycie hitArea ............................................................................................................................303 Użycie hitTestPoint ....................................................................................................................304 Zamiana kolejności potomków ...................................................................................................304 Ponowne przypisywanie rodzica wyświetlanym obiektom .........................................................305 Praca z MovieClip .............................................................................................................................306 Użycie stop() oraz gotoAndPlay() ..............................................................................................307 Właściwości totalFrames oraz framesLoaded .............................................................................307 Przykłady zastosowania listy wyświetlania ......................................................................................308 Tworzenie procedury renderującej element ................................................................................308 Tworzenie odbijającej się piłki ...................................................................................................309 Sprawdzanie wystąpienia kolizji .................................................................................................310 Podsumowanie ..................................................................................................................................312
14
ActionScript 3.0. Biblia Rozdział 13. Praca z obiektami DisplayObject w programie Flash CS3 ........ 315 Tworzenie symboli w programie Flash CS3 .....................................................................................315 Ustawienie nazwy zmiennej Twojego symbolu na stole montażowym ......................................318 Wykorzystanie własnej klasy dla własnego symbolu .................................................................319 Osadzanie w programie grafik bitmapowych ..............................................................................319 Osadzanie grafik w programie Flex ............................................................................................320 Dostęp do osadzonych klas graficznych ...........................................................................................320 Podsumowanie ..................................................................................................................................321 Rozdział 14. Drukowanie ................................................................................. 323 Po co drukować z Flasha? .................................................................................................................323 Sterowanie wydrukiem z Flasha .......................................................................................................325 Wprowadzenie do klasy PrintJob ................................................................................................325 Uruchomienie żądania drukowania .............................................................................................326 Określanie celu drukowania oraz jego opcji formatujących .......................................................327 Potencjalne problemy podczas drukowania we Flashu ...............................................................329 Dodawanie funkcji drukowania do aplikacji .....................................................................................330 Podsumowanie ..................................................................................................................................334 Rozdział 15. Praca z tekstem i czcionkami .................................................... 335 Pola tekstowe ....................................................................................................................................336 Tworzenie nowego pola tekstowego ...........................................................................................336 Dodanie tekstu do pola tekstowego ............................................................................................336 Ustawianie rozmiaru pola tekstowego ........................................................................................336 Ustawianie skali oraz obrotu pola tekstowego ............................................................................337 Pobieranie łańcuchów znaków z pola tekstowego ......................................................................338 Wyświetlanie HTML ..................................................................................................................338 Dodawanie obrazów lub plików SWF do pola tekstowego za pomocą ...........................340 Arkusz stylu dla pola tekstowego ...............................................................................................341 Tworzenie tła dla pola tekstowego ..............................................................................................341 Formatowanie tekstu .........................................................................................................................342 Tworzenie i użycie obiektu TextFormat dla pola tekstowego .....................................................342 Bardziej zaawansowana kontrola nad tekstem ..................................................................................349 Osadzanie czcionek i antyaliasing ..............................................................................................349 Właściwość gridFitType .............................................................................................................350 Właściwości numLines oraz wordWrap .....................................................................................351 Ograniczenia znaków pola tekstowego .......................................................................................352 Właściwości przewijania ............................................................................................................353 Wielowierszowość i wymiary tekstu ..........................................................................................354 Wyświetlanie Unicode ................................................................................................................356 Tworzenie wejściowych pól tekstowych ...........................................................................................357 Uczynienie pola tekstowego wejściowym ..................................................................................357 Tabulatory dla wejściowych pól tekstowych ..............................................................................358 Nasłuchiwanie zdarzeń pola tekstowego ...........................................................................................358 Zdarzenie textInput .....................................................................................................................358 Zdarzenia change i scroll ............................................................................................................359 Zdarzenia focusIn oraz focusOut ................................................................................................360 Zdarzenia link .............................................................................................................................360 Podsumowanie ..................................................................................................................................361
Spis treści
15
Część IV System obsługi zdarzeń ................................................ 363 Rozdział 16. Działanie zdarzeń ....................................................................... 365 Podstawy działania zdarzeń ..............................................................................................................365 Zdarzenia sobotniego poranku ....................................................................................................366 Terminologia zdarzeń .................................................................................................................368 Klasa EventDispatcher ......................................................................................................................369 Używanie EventDispatcher .........................................................................................................369 Używanie EventDispatcher poprzez kompozycję .......................................................................373 Praca z obiektami Event ....................................................................................................................374 Tworzenie nowego zdarzenia .....................................................................................................375 Dodawanie i usuwanie obiektów nasłuchujących zdarzeń ................................................................376 Usuwanie obiektu nasłuchującego z dyspozytora .......................................................................377 Strumień zdarzenia ............................................................................................................................378 Fazy strumienia zdarzenia ..........................................................................................................378 Strumień zdarzenia w praktyce ...................................................................................................381 Zapobieganie domyślnym zachowaniom ..........................................................................................383 Podsumowanie ..................................................................................................................................384 Rozdział 17. Praca ze zdarzeniami myszy i klawiatury ................................. 385 Podstawy zdarzeń MouseEvent .........................................................................................................385 Współrzędne lokalne i sceny .............................................................................................................388 Inne właściwości zdarzeń MouseEvent .............................................................................................390 Typy zdarzeń MouseEvent ................................................................................................................390 MouseEvent.CLICK ...................................................................................................................391 MouseEvent.DOUBLE_CLICK .................................................................................................391 MouseEvent.MOUSE_DOWN ...................................................................................................391 MouseEvent.MOUSE_MOVE ....................................................................................................391 MouseEvent.MOUSE_OUT .......................................................................................................391 MouseEvent.MOUSE_OVER ....................................................................................................392 MouseEvent.MOUSE_UP ..........................................................................................................392 MouseEvent.MOUSE_WHEEL .................................................................................................392 MouseEvent.ROLL_OUT ...........................................................................................................392 Mouse Event.ROLL_OVER .......................................................................................................392 FocusEvent.MOUSE_FOCUS_CHANGE .................................................................................393 Używanie zdarzeń MouseEvent w połączeniu z użyciem myszy ......................................................394 Podstawy zdarzeń KeyboardEvent ....................................................................................................395 Typy zdarzeń KeyboardEvent ...........................................................................................................396 FocusEvent.KEY_FOCUS_CHANGE .......................................................................................397 Zastosowanie właściwości keyCode .................................................................................................397 Zdarzenia IMEEvent .........................................................................................................................398 Podsumowanie ..................................................................................................................................399 Rozdział 18. Korzystanie z czasomierzy ........................................................ 401 Podstawy pracy z czasomierzem .......................................................................................................402 Tworzenie czasomierza ...............................................................................................................402 Nasłuchiwanie zdarzeń czasomierza ...........................................................................................403 Uruchamianie, zatrzymywanie i resetowanie czasomierza .........................................................403 Obsługa zdarzenia TimerEvent .........................................................................................................404 Uzyskanie referencji do czasomierza ..........................................................................................405 Opóźnianie wykonania funkcji ...................................................................................................406
16
ActionScript 3.0. Biblia Tworzenie zegara światowego ..........................................................................................................407 Bonus ..........................................................................................................................................408 Starsze funkcje czasomierza .............................................................................................................408 Użycie getTimer() .......................................................................................................................409 Podsumowanie ..................................................................................................................................409
Część V Obsługa błędów ............................................................. 411 Rozdział 19. Czym są błędy ............................................................................. 413 Drogi wystąpienia błędu ...................................................................................................................413 Korzystanie z wyjątków ....................................................................................................................414 Rzucanie wyjątków .....................................................................................................................415 Przechwytywanie wyjątków .......................................................................................................415 Przepływ wyjątku .......................................................................................................................417 Wyjątki nieprzechwycone ...........................................................................................................419 finally ..........................................................................................................................................420 Ponowne rzucanie wyjątków ......................................................................................................421 Przechwytywanie błędów generowanych przez Flasha .....................................................................421 Własne wyjątki .................................................................................................................................423 Obsługa błędów asynchronicznych ...................................................................................................424 Podsumowanie ..................................................................................................................................425 Rozdział 20. Korzystanie z debugera AVM2 ................................................... 427 Wprowadzenie do debugowania .......................................................................................................427 Uruchamianie debugera ....................................................................................................................428 Uruchamianie i zatrzymywanie debugera Flash CS3 ..................................................................429 Uruchamianie i zatrzymywanie debugera Flex Builder 3 ...........................................................430 Porównanie debugerów ...............................................................................................................432 Przejmowanie sterowania nad wykonywaniem .................................................................................433 Zatrzymanie przy nieprzechwyconym wyjątku ..........................................................................433 Zatrzymanie w punkcie przerwania ............................................................................................434 Zatrzymanie na żądanie ..............................................................................................................435 Odsłaniając kurtynę ..........................................................................................................................436 Interpretacja panelu Zmienne .....................................................................................................437 Panel Variables w programie Flex Builder i wyrażenia czujki ...................................................438 Nawigowanie po kodzie ....................................................................................................................439 Kontynuuj ...................................................................................................................................439 Stos wywołań ..............................................................................................................................440 Wejdź (step into) .........................................................................................................................441 Wykonaj krokowo (step over) ....................................................................................................442 Wyjdź lub return .........................................................................................................................442 Debugowanie prostego przykładu .....................................................................................................443 Efektywne wykorzystanie debugera ..................................................................................................445 Podsumowanie ..................................................................................................................................446 Rozdział 21. Aplikacja tolerancyjna na błędy ................................................ 449 Tworzenie strategii ............................................................................................................................449 Określanie błędów do obsługi ...........................................................................................................450 Określanie kategorii błędu ................................................................................................................451 Logi błędów ......................................................................................................................................452 Informowanie użytkownika ..............................................................................................................454
Spis treści
17
Degradacja stylów — przykład .........................................................................................................455 Podsumowanie ..................................................................................................................................457
Część VI Praca z danymi zewnętrznymi ..................................... 459 Rozdział 22. Podstawy pracy w sieci ............................................................. 461 URLRequest ......................................................................................................................................462 navigateToURL() ........................................................................................................................463 GET i POST ................................................................................................................................463 URLLoader .......................................................................................................................................465 Podstawy URLLoader .................................................................................................................465 Dalsze wykorzystanie URLLoader .............................................................................................468 URLVariables .............................................................................................................................469 URLStream .................................................................................................................................470 sendToURL() ..............................................................................................................................471 Użycie obiektu Loader ......................................................................................................................471 Bezpieczeństwo w programie Flash Player .................................................................................474 Podsumowanie ..................................................................................................................................476 Rozdział 23. Komunikacja z technologiami serwerowymi ............................ 479 Komunikacja poprzez URLLoader ...................................................................................................479 Wykorzystanie PHP w komunikacji z Flashem ................................................................................480 Wysyłanie danych bez ich wczytywania .....................................................................................481 XMLSocket .......................................................................................................................................481 Tworzenie obiektu XMLSocket ..................................................................................................482 Strona serwerowa XMLSocket ...................................................................................................482 Flash Remoting .................................................................................................................................483 NetConnection ............................................................................................................................484 Responder ...................................................................................................................................485 Rozwiązania serwerowe Flash Remoting ...................................................................................486 Podsumowanie ..................................................................................................................................488 Rozdział 24. Zapisywanie danych na lokalnym komputerze za pomocą SharedObject ........................................................... 489 Porównanie sposobów trwałego przechowywania ............................................................................489 Przechowywanie informacji w lokalnym obiekcie współdzielonym ..........................................490 Przechowywanie informacji na serwerze ....................................................................................490 Przechowywanie informacji w plikach cookie przeglądarki .......................................................490 Sytuacje odpowiednie do zastosowania obiektów współdzielonych .................................................491 Użycie obiektów SharedObject .........................................................................................................492 Uzyskiwanie obiektu SharedObject ............................................................................................492 Odczyt i zapis w SharedObject ...................................................................................................493 Usuwanie informacji z SharedObject ..........................................................................................493 Zapisywanie informacji ..............................................................................................................494 Udostępnianie danych pomiędzy plikami SWF ................................................................................494 Żądanie bezpiecznego połączenia ...............................................................................................495 Udostępnianie obiektów współdzielonych plikom SWF ActionScript 1.0 oraz 2.0 ....................496 Praca z ograniczeniami rozmiaru ......................................................................................................497 Ustawienia użytkownika .............................................................................................................497 Żądania użytkownika ..................................................................................................................498 Pytanie o miejsce zanim będzie za późno ...................................................................................499
18
ActionScript 3.0. Biblia Wcześniejsze pytanie o miejsce ..................................................................................................499 Korzystanie z flush() ...................................................................................................................500 Wyświetlanie wykorzystywanego miejsca .................................................................................501 Przechowywanie własnych klas ........................................................................................................501 Zapisywanie własnych klas bez modyfikacji ..............................................................................501 Tworzenie klas samoserializujących ...........................................................................................503 Użycie koncpecji serializacji dla wywołań zwrotnych ......................................................................505 Podsumowanie ..................................................................................................................................505 Rozdział 25. Zarządzanie wysyłaniem i pobieraniem plików ....................... 507 FileReference ....................................................................................................................................507 Wysyłanie plików .............................................................................................................................508 Wybór pliku do wysłania ............................................................................................................508 Określanie, czy plik został wybrany ...........................................................................................509 Pobieranie właściwości pliku ......................................................................................................510 Wysyłanie pliku ..........................................................................................................................512 Dodanie do aplikacji możliwości wysyłania plików .........................................................................513 Pobieranie pliku ................................................................................................................................515 Podsumowanie ..................................................................................................................................516
Część VII Wzbogacanie programów dźwiękiem i filmem .......... 517 Rozdział 26. Praca z dźwiękiem ...................................................................... 519 Jak działa dźwięk w AS3 ..................................................................................................................519 Poznanie klas dźwiękowych AS3 ...............................................................................................519 Praca ze zdarzeniami dźwiękowymi ...........................................................................................520 Tworzenie obiektu Sound .................................................................................................................521 Wczytywanie pliku z zewnętrznego pliku lub URL ...................................................................521 Osadzanie dźwięków w programie .............................................................................................523 Kontrola odtwarzania dźwięku .........................................................................................................524 Odtwarzanie i zatrzymywanie dźwięku ......................................................................................524 Ustawianie punktu początkowego oraz liczby pętli dźwiękowych .............................................525 Przewijanie do przodu, do tyłu, wstrzymywanie i ponowne rozpoczynanie odtwarzania ...........525 Stosowanie transformacji dźwiękowych ...........................................................................................528 Zmiana głośności i balansu dźwięku ..........................................................................................528 Praca z metadanymi dźwięku ............................................................................................................529 Sprawdzanie rozmiaru pliku dźwiękowego ................................................................................529 Uzyskiwanie danych ID3 utworu ................................................................................................529 Obliczanie danych spektrum .............................................................................................................530 Wykrywanie możliwości dźwiękowych ............................................................................................532 Podsumowanie ..................................................................................................................................533 Rozdział 27. Dodawanie wideo ....................................................................... 535 Praca z plikami Flash Video .............................................................................................................535 Tworzenie pliku FLV za pomocą aplikacji Flash Video Encoder ...............................................535 Użycie RTMP do uzyskiwania dostępu do plików FLV .............................................................536 Użycie HTTP do uzyskania dostępu do plików FLV ..................................................................537 Klasa Video oraz NetStream .............................................................................................................538 Wczytywanie plików FLV do filmu Flash ........................................................................................540 Utworzenie połączenia HTTP prowadzącego do pliku FLV .......................................................541 Wyświetlanie danych NetStream w obiekcie Video ...................................................................541
Spis treści
19
Sprawdzanie informacji statusu klasy NetStream .......................................................................541 Uzyskiwanie metadanych ...........................................................................................................542 Tworzenie aplikacji odtwarzającej pliki FLV ...................................................................................543 Podsumowanie ..................................................................................................................................548 Rozdział 28. Dostęp do mikrofonów i kamer ................................................ 549 Kamera ..............................................................................................................................................549 Podłączanie kamery do obiektu Video ........................................................................................551 Mikrofon ...........................................................................................................................................552 Praca z mikrofonem ....................................................................................................................553 Serwery multimedialne .....................................................................................................................555 Podsumowanie ..................................................................................................................................556
Część VIII Programowanie grafiki i ruchu .................................. 557 Rozdział 29. Stosowanie filtrów do grafik .................................................... 559 Podstawy filtrów ...............................................................................................................................560 Stosowanie filtrów ............................................................................................................................561 Filtry rozmycia ...........................................................................................................................561 Dodawanie filtru cienia ...............................................................................................................562 Filtry fazy ...................................................................................................................................563 Filtry blasku ................................................................................................................................565 Dodawanie filtru fazy gradientowej ............................................................................................566 Filtr macierzy kolorów ................................................................................................................568 Dodawanie filtru konwolucyjnego ..............................................................................................574 Filtr blasku gradientowego .........................................................................................................575 Filtr mapy przemieszczeń ...........................................................................................................575 Dodawanie więcej niż jednego filtru .................................................................................................576 Obracanie obiektów z filtrami ...........................................................................................................577 Podsumowanie ..................................................................................................................................579 Rozdział 30. Programowe rysowanie grafik wektorowych ......................... 581 Linie i style linii ................................................................................................................................581 Ustawianie stylu linii ..................................................................................................................582 Przesuwanie pióra bez rysowania ...............................................................................................585 Rysowanie prostej linii ...............................................................................................................585 Rysowanie krzywej .....................................................................................................................586 Dodanie prostego, jednokolorowego wypełnienia ......................................................................587 Dodawanie wypełnienia bitmapowego .......................................................................................588 Praca z gradientami .....................................................................................................................590 Czyszczenie wcześniej narysowanej grafiki ...............................................................................594 Tworzenie kształtów .........................................................................................................................594 Rysowanie okręgów ....................................................................................................................595 Rysowanie elips ..........................................................................................................................596 Rysowanie zaokrąglonych prostokątów ......................................................................................596 Wypełnianie kształtów ................................................................................................................597 Maski ................................................................................................................................................599 Zaawansowane maski .................................................................................................................600 Tworzenie aplikacji rysującej ...........................................................................................................601 Podsumowanie ..................................................................................................................................607
20
ActionScript 3.0. Biblia Rozdział 31. Programowanie animacji ........................................................... 609 Flash Player a animacja .....................................................................................................................609 Szybkość odtwarzania .................................................................................................................609 Działanie programu Flash Player ................................................................................................610 Animowanie za pomocą samego ActionScript ..................................................................................612 Animowanie za pomocą czasu ....................................................................................................612 Animowanie za pomocą klatek ...................................................................................................613 Animacja i prędkość ...................................................................................................................614 Animowanie w programie Flash .......................................................................................................615 Przegląd: animacje automatyczne, klatki kluczowe i zmiana dynamiki .....................................616 XML ruchu .................................................................................................................................617 Użycie pakietu ruchu Flasha .......................................................................................................625 Animowanie za pomocą środowiska Flex .........................................................................................627 Zestawy animacyjne firm trzecich ....................................................................................................627 Pakiet ruchu Flash .......................................................................................................................627 Tweener ......................................................................................................................................628 AnimationPackage ......................................................................................................................628 Podsumowanie ..................................................................................................................................629 Rozdział 32. Transformacje grafiki ................................................................ 631 Praca z transformacjami macierzowymi ...........................................................................................631 Wykorzystanie transformacji macierzowych ..............................................................................635 Praca z transformacjami kolorów ......................................................................................................637 Stosowanie transformacji kolorów ..............................................................................................638 Pobieranie i ustawianie koloru ....................................................................................................638 Użycie tint dla obiektów wyświetlanych ....................................................................................639 Przywracanie kolorów ................................................................................................................640 Transformacja kolorów ...............................................................................................................640 Podsumowanie ..................................................................................................................................641 Rozdział 33. Programowe rysowanie grafik bitmapowych ......................... 643 Tworzenie obiektu BitmapData ........................................................................................................643 Metoda wykorzystująca konstruktor ...........................................................................................644 Tworzenie instancji osadzonych zasobów ..................................................................................644 Wyświetlanie obrazów BitmapData ............................................................................................646 Praca z właściwościami BitmapData ................................................................................................647 Kopiowanie obrazów ........................................................................................................................647 Kopiowanie z obiektów wyświetlanych ......................................................................................648 Wczytywanie obrazów BitmapData ............................................................................................651 Kopiowanie z obiektów BitmapData ..........................................................................................653 Stosowanie transformacji kolorów ....................................................................................................661 Stosowanie wypełnień .......................................................................................................................662 Stosowanie wypełnień prostokątnych .........................................................................................662 Stosowanie wypełnień zalewanych .............................................................................................662 Wyznaczanie obszarów według koloru .............................................................................................663 Stosowanie efektów ..........................................................................................................................664 Zastępowanie kolorów z wykorzystaniem progu ........................................................................664 Używanie przenikania pikseli .....................................................................................................666 Ponowne mapowanie palety kolorów .........................................................................................669
Spis treści
21
Tworzenie szumu ..............................................................................................................................670 Dodawanie szumu .......................................................................................................................670 Dodawanie szumu Perlina ..........................................................................................................673 Stosowanie filtrów ............................................................................................................................676 Podsumowanie ..................................................................................................................................678
Część IX Praca z danymi binarnymi ............................................ 679 Rozdział 34. Praca z danymi binarnymi ......................................................... 681 Tworzenie tablicy bajtów ..................................................................................................................681 Zapis do tablicy bajtów .....................................................................................................................682 Odczyt z tablicy bajtów .....................................................................................................................682 Powszechne zastosowania tablic bajtów ...........................................................................................683 Wyznaczanie spektrum dźwiękowego ........................................................................................683 Wczytywanie obrazów ................................................................................................................684 Kopiowanie obiektów .................................................................................................................685 Własna serializacja danych .........................................................................................................686 Praca z binarnymi gniazdami ......................................................................................................688 Podsumowanie ..................................................................................................................................691
Część X Wdrażanie programu ..................................................... 693 Rozdział 35. Wdrażanie zawartości Flash ...................................................... 695 Osadzanie treści Flash na stronie internetowej .................................................................................695 Osadzanie treści Flash za pomocą SWFObject .................................................................................697 Ustawianie opcji programu Flash Player ...........................................................................................698 Przezroczysty Flash ....................................................................................................................699 Pełnoekranowy Flash ..................................................................................................................699 Przekazywanie zmiennych do pliku SWF .........................................................................................700 Automatyczne uaktualnianie programu Flash Player ........................................................................701 Podsumowanie ..................................................................................................................................702 Rozdział 36. Komunikacja z JavaScript .......................................................... 703 Komunikacja pomiędzy JavaSript a Flashem ....................................................................................703 Klasa ExternalInterface ...............................................................................................................703 Podsumowanie ..................................................................................................................................707 Rozdział 37. Wykorzystanie połączeń lokalnych do komunikacji pomiędzy filmami Flash ............................................................. 709 Tworzenie aplikacji wysyłającej .......................................................................................................709 Wysyłanie parametrów ...............................................................................................................710 Sprawdzanie statusu wysyłania ...................................................................................................710 Tworzenie aplikacji odbierającej ......................................................................................................710 Wysyłanie i odbieranie pomiędzy domenami ...................................................................................711 Aplikacja wysyłająca ..................................................................................................................712 Aplikacja odbierająca .................................................................................................................712 Podsumowanie ..................................................................................................................................713 Skorowidz ...................................................................................................... 715
22
ActionScript 3.0. Biblia
O autorach Roger Braunstein został ostatnio mianowany dyrektorem ds. technologii w firmie Your Majesty w Nowym Jorku. Mieszka i jeździ na rowerze w Brooklynie, co daje mu nieograniczoną możliwość przeżywania różnego rodzaju przygód. Interesuje się wszystkim — w tym filmem, gotowaniem, muzyką 8-bitową oraz latem. O bardziej szczegółową listę zainteresowań musisz zapytać go osobiście. Desperacko próbuje nauczyć się więcej, niż jest to możliwe, ale samo próbowanie jest dla niego przygodą. Roger jest przeważnie zawstydzony stanem jego strony internetowej http://partlyhuman.com/. Mims H. Wright (tak, to jego prawdziwe imię) urodził się jako dociekliwy chłopiec z Południa. W wieku trzynastu lat został „przetransplantowany” do Boulder w stanie Colorado, gdzie na szczęście ominął go etap bycia kudłatym nastolatkiem w koszulce polo. Będąc ekspertem w odnajdywaniu porządku w chaosie i odwrotnie, podążył za swoją pasją projektowania i po ukończeniu szkoły średniej rozpoczął edukację w wyższej szkole artystycznej, chcąc uniknąć wszelakiej prawdziwej odpowiedzialności. W momencie boomu internetowego dołączył do szeregów dwudziestolatków, aby dostać swój kawałek tego tortu, jednak szybko zorientował się, że niewiele zostało, a do tego wcale nie jest to smaczne. Po powrocie do szkoły, wynikającym z potrzeby bezpieczeństwa, pomagał w kształtowaniu interaktywnego programu projektowego na Uniwersytecie w Kolorado. W roku 2002 przeniósł się do Nowego Jorku, gdzie podjął pracę jako production artist, został nagrodzony za swoje osiągnięcia i szybko stał się znanym autorytetem w dziedzinie projektowania i programowania we Flashu. Od tego czasu pracował z wieloma klientami, od malutkich firm po wielkie przedsiębiorstwa, i, o czym sam mówi z dużą dozą ironii, osiągnął status weterana jeszcze przed trzydziestką. Mims obecnie żyje w Brooklynie w Nowym Jorku, gdzie tworzy strony internetowe w celach zarobkowych. Jest również amatorskim kierowcą wyścigowym. Joshua J. Noble mieszka w Buenos Aires w Argentynie. W wolnym czasie szlifuje swój hiszpański, chiński, C, Erlang oraz Ruby; ogląda piłkę nożną; czyta tyle gazet, ile tylko może; wygłupia się z bibliotekami wizji komputerowej; i bawi się minikontrolerami. Pracował z ActionScript przez cztery lata dla różnych firm i przy różnych projektach.
24
ActionScript 3.0. Biblia
Podziękowania Ta książka była przede wszystkim wysiłkiem zespołowym. Współautorzy, redaktorzy i ekipa wydawnictwa Wiley zasługują na ogromne brawa. Dziękuję Mimsowi za to, że był niesamowicie zorganizowany i ambitny. Joey, nigdy chyba nie zrozumiem, skąd wziąłeś czas na pracę nad tą książką, ale jest o wiele lepsza dzięki Twojemu doświadczeniu i wkładowi. Dziękuję Ci. Josh, praca z Tobą była dla mnie przyjemnością, dziękuję Ci za to, że podejmowałeś się najtrudniejszych zadań. Naszemu redaktorowi, Chrisowi Webbowi, dziękuję za tę niesamowitą szansę oraz za wspieranie nas podczas całej tej przygody. Adaobi Obi Tulton, Twój wkład w tę książkę znacznie wykracza poza Twój tytuł redaktora. Byłeś motywatorem, organizatorem, menedżerem i przyjacielem. Nie dalibyśmy bez Ciebie rady. Do naszych redaktorów technicznych, Marka Waltersa, Corey Szopinski oraz Marka Llobrery, kieruję podziękowania za Wasz sokoli wzrok i bezcenne wskazówki. Dziękuję Schematic za wspieranie mnie bardziej, niż mogłem się tego spodziewać. Dziękuję również wszystkim moim współpracownikom w Schematic za bycie mentorami i wspaniałymi przyjaciółmi, zwłaszcza Robertowi Reinhardtowi, Danny’emu Pettersonowi oraz Joeyowi Lottowi. Dziękuję za wiedzę i udzielane ciągle porady oraz zawsze otwarte przede mną drzwi. Dziękuję moim przyjaciołom i rodzinie za tolerowanie mojej okazyjnej samoizolacji oraz innych antyspołecznych zachowań. Na koniec chcę podziękować moim rodzicom za motywowanie mnie, gdy potrzebowałem tego najbardziej, i przekazanie mi najpotrzebniejszych darów. — Roger Braunstein Napisanie książki ActionScript. Biblia od podstaw było ogromnym przedsięwzięciem, wymagającym pomocy wielu ludzi. Chciałbym podziękować naszym redaktorom w Wiley, Chrisowi Webbowi oraz Adaobiemu Obi Tultonowi; naszym redaktorom technicznym, Corey Szopinski, Markowi Waltersowi oraz Markowi Llobrerze; i oczywiście pozostałym autorom. Roger, byłeś wspaniałym przyjacielem i partnerem podczas pracy nad tym projektem i wszystkimi innymi. Mam nadzieję na kolejne okazje współpracy w przyszłości. Mojej rodzinie i przyjaciołom, którym wielokrotnie zwiewałem podczas pisania i którzy cierpieli z powodu mojej drażliwości wynikającej z braku snu, dziękuję za cierpliwość. Dziękuję Schematic za elastyczność w stosunku do mojego kalendarza pracy i możliwość jego dostosowywania. Dziękuję czytelnikom mojego blogu i kolegom, którzy pomagali w omawianiu niepewnych zagadnień. Dziękuję testerom próbnych wersji Flash CS3 oraz wszystkim w Adobe za uczynienie tych niesamowitych produktów jeszcze lepszymi. Wielkie dzięki dla J.S., J.M., K.H., D.S., D.W., J.N., C.S., J.C., C.D., N.W., L.W., V.H., M.W., J.Y., D.P., J B., S.P., C.Z., A.C., W.J. oraz Ciebie!
26
ActionScript 3.0. Biblia
Przede wszystkim dziękuję jednak Robertowi Reinhardtowi oraz Joeyowi Lottowi za przekazanie nam pałeczki. Na pewno zmieni to moje życie. — Mims H. Wright Dziękuję Chrisowi Webbowi oraz Adaobiemu Obi Tultonowi i Corey Szopinski, Markowi Waltersowi oraz Markowi Llobrerze za redakcję techniczną, a Joeyowi, Rogerowi i Mimsowi za zaproszenie mnie do współpracy. To było niesamowite. Chciałbym podziękować Morganowi Schwartzowi za tak cierpliwe wprowadzenie mnie przed wieloma laty w sztukę Flash Remoting, PHP oraz MySQL i nauczenie mnie cieszenia się ulepszaniem skryptów i serwerów oraz tego, że nie narzędzia są najważniejsze, ale to, jak je wykorzystasz. Chciałbym też podziękować Abeyowi George’owi za przyswojenie mi tak wielu rzeczy na temat programowania we Fleksie oraz programowania obiektowego, a także za pokazanie, jak pozostawać spokojnym na krótko przed ostatecznym terminem oddania pracy oraz co znaczy pisanie pięknego kodu i logiczne myślenie. — Joshua J. Noble
Wstęp Witaj w ActionScript 3.0. Biblia. Książka ta stawia sobie za cel bycie wyczerpującym źródłem informacji na temat wszystkich zagadnień związanych z ActionScript 3.0. Możesz korzystać z niej jak z książki instruktażowej lub jak z podręcznika, lub na oba sposoby. Książka przedstawia koncepcje zastosowane w ActionScript 3.0 zarówno w postaci informacji z zaplecza, jak i rzeczywistych przykładów mających za zadanie polepszenie Twojego zrozumienia zagadnień związanych z ActionScript 3.0. Uzbrojony w wiedzę zawartą w tej książce będziesz w stanie urzeczywistnić swoje pomysły w ActionScript 3.0, niezależnie od tego czy są aplikacjami, stronami internetowymi, prezentacjami, grami, zabawkami, sztuką, czy eksperymentami. ActionScript 3.0 jest językiem stosowanym w kilku narzędziach stworzonych przez firmę Adobe, wliczając w to Flash CS3, Flex 2 i 3 oraz AIR (wcześniej Apollo). Nie ma więc znaczenia, którego z tych narzędzi będziesz używać podczas swojej pracy z niniejszą książką. Nie opisano tematów specyficznych dla programów Flash CS3 czy Flex 3, ale zagadnienia wspólne dla obu tych aplikacji. W przypadkach, gdy narzędzia te obsługują jakiś element języka w odmienny sposób, wyraźnie wskazujemy na te różnice. Jako pomoc w nauce programowania niniejsza książka jest odpowiednia dla każdego z tych narzędzi. ActionScript 3.0 oparty został na standardach sieciowych. Implementuje wybrane elementy ze specyfikacji języka ECMAScript (wydanie 4.), jak również zdarzenia E4X oraz DOM level 3. Oznacza to, że nauka programowania w języku ActionScript 3.0 przygotuje Cię również do roli programisty innych języków ECMAScript, szczególnie nowych wersji JavaScript.
ActionScript a książki z serii Flash. Biblia ActionScript 3.0 jest trzecią pełną wersją języka ActionScript. Wcześniej ActionScript był używany głównie do tworzenia zawartości Flash. Teraz ActionScript, będący dojrzałą, niezależną technologią, stosowany jest w coraz bardziej różnorodnych kontekstach. Uwzględniono to w niniejszej książce, podczas gdy inne pozycje wydawnictwa Helion dostarczą Ci ćwiczeń skoncentrowanych na narzędziach, które wykorzystują ActionScript 3.0. Kontynuację serii Flash. Biblia stanowi pozycja Adobe Flash CS3/CS3 PL Professional. Biblia (Helion, 2009). Książka ta dostarcza wszelkich informacji na temat najnowszej wersji programu Flash, chociażby jak korzystać z osi czasu, bibliotek czy narzędzi
28
ActionScript 3.0. Biblia
rysujących. Seria Flash. Biblia może wprowadzić Cię do ActionScript, ale nie jest obszernym źródłem informacji na temat programowania we Flashu. Jeśli masz zamiar korzystać z Flasha do tworzenia projektów ActionScript, sugerujemy Ci, abyś książkę Adobe Flash CS3/CS3 PL Professional. Biblia traktował jako uzupełnienie niniejszej. Jeśli używasz ActionScript 3.0 z programem Flex, to jako uzupełnienie naszej książki polecamy na przykład pozycję Adobe Flex 3. Oficjalny podręcznik (Helion, w przygotowaniu). Pojawiło się również kilka książek na temat programu Adobe AIR, które mogą uzupełniać naszą książkę, jeśli tworzysz aplikacje biurkowe z wykorzystaniem ActionScript 3.0 oraz AIR. Polecamy pozycje Adobe AIR dla programistów JavaScript. Leksykon kieszonkowy (Helion, 2008) oraz anglojęzyczne Adobe AIR Instant Results (Wrox, 2007) i Professional AIR (Wrox, 2007).
Dla kogo jest ta książka Jeśli interesujesz się tworzeniem stron internetowych oraz aplikacji za pomocą programu Flex Builder lub masz zamiar poznać potężne funkcje programowania w programie Flash CS3, ActionScript 3.0. Biblia jest książką dla Ciebie. Wprowadzi Cię do ActionScript 3.0, zaczynając od podstaw tego języka, a kończąc na jego potężnych nowych zaawansowanych funkcjach. Jeśli chcesz poznać nowe funkcje programu Adobe Flash CS3 Professional lub nauczyć się z niego korzystać, książka ta może nie być dla Ciebie odpowiednia. Nie opisuje ona narzędzia Flash, nie odpowie Ci także na pytania, jak skonfigurować własne pliki Flash oraz gdzie we Flashu można znaleźć odpowiednie polecenia i narzędzia. Chcąc dowiedzieć się więcej na temat tego programu, sięgnij po pozycje przedstawione w poprzednim podrozdziale. Jeśli masz zamiar tworzyć aplikacje za pomocą programu Flex, miej na uwadze, że pomimo iż niniejsza książka da Ci odpowiednie przygotowanie wstępne — solidną wiedzę na temat ActionScript 3.0 — nie znajdziesz w niej jednak opisu budowy i działania programu Flex lub MXML. Chcąc dowiedzieć się więcej na temat tej technologii, sięgnij po książki przedstawione w poprzednim podrozdziale. Framework programu Flex jest predefiniowaną biblioteką komponentów, które są wspólnie używane podczas tworzenia aplikacji Flash. Framework ten jest zbudowany w ActionScript 3.0 i z niego korzysta, nie został jednak opisany w tej książce.
Niezależnie od tego, czy posiadasz jakieś doświadczenie z wcześniejszymi wersjami ActionScript, książka ta jest dla Ciebie odpowiednią lekturą. ActionScript 3.0 jeszcze bardziej niż ActionScript 2.0 wymaga podejścia obiektowego, a niniejsza książka zawiera elementarz programowania obiektowego. Znajdziesz w niej wszystko, co potrzebne jest do szybkiego poznania zasad programowania obiektowego. Niezależnie od poziomu Twoich umiejętności i doświadczenia jej lektura da Ci to, co jest niezbędne do rozpoczęcia pracy z ActionScript 3.0. W treści szczególnie podkreślono różnice pomiędzy ActionScript 2.0 a ActionScript 3.0, dlatego jeśli masz zamiar uaktualnić swoją wiedzę na temat tego języka programowania,
Wstęp
29
my Ci w tym pomożemy. Zwracamy uwagę na zagadnienia, które uległy zmianie w ostatniej wersji języka ActionScript. Dzięki temu nie przeoczysz żadnej z mniej oraz bardziej subtelnych różnic.
Jak zorganizowana jest książka Zwrot „ActionScript 3.0” odnosi się zarówno do języka programowania, jak i API (Application Programing Interface — interfejs programowania aplikacji) programu Flash Player 9. Tworzenie w języku ActionScript 3.0 pozwoli Ci pisać programy, jednak to API programu Flash Player — biblioteka klas oraz funkcji, które są dostępne dla wszystkich programów ActionScript 3.0 — umożliwi Ci tworzenie programów z aspektem wizualnym, animowanych, reagujących na zdarzenia klawiatury i myszy, odtwarzających filmy i dźwięki, łączących się z internetem, a także pozwoli Ci na robienie wielu innych rzeczy. Będziesz musiał sobie również poradzić z zagadnieniami związanymi z programowaniem w ActionScript, a niebędącymi — jak np. debugowanie — częściami samego języka lub API. Zagadnienia związane z językiem ActionScript 3.0, API programu Flash Player 9 oraz innymi pokrewnymi tematami zostały w tej książce pogrupowane w jej poszczególnych częściach. Części zawierają w sobie podobne tematy oraz powiązane ze sobą funkcjonalności. Są dzielone na rozdziały, z których każdy skupia się na pojedynczym zagadnieniu. Wydaje nam się, że książka ta jest zorganizowana dobrze od początku do końca, jednak po zapoznaniu się z podstawami języka w części I możesz przejść do dowolnego, interesującego Cię w danym momencie tematu. Niektóre tematy mogą wymagać znajomości wcześniejszych rozdziałów, jednak w takich przypadkach znajdziesz do nich odwołania.
Część I: „Rozpoczęcie pracy z ActionScript 3.0” Część I tej książki, składająca się z rozdziałów od 1. do 5, została zaprojektowana z myślą o wyrównaniu poziomu wiedzy czytelników. Ta część pokaże Ci, o co tak właściwie chodzi w ActionScript 3.0 oraz jak używać tego języka. Pod koniec tej części powinieneś być już w stanie pisać programy w ActionScript 3.0. Poznasz:
ActionScript 3.0 — czym jest, do czego służy, co w nim nowego.
ActionScript 3.0 — język programowania.
Podstawy programowania: wyrażenia, zmienne, operatory, funkcje, zasięgi.
Programowanie obiektowe: klasy, interfejsy, dziedziczenie, kompozycję.
Jeśli ActionScript 3.0 jest Twoim pierwszym językiem programowania, przeczytaj dokładnie całą część I. Rozdział 1. „Wprowadzenie do ActionScript 3.0” powinien przeczytać każdy. Jeśli masz już doświadczenie w programowaniu obiektowym w innych językach, powinieneś przeczytać część I, aby wyczuć składnię i funkcję tego języka. Jeśli znasz dobrze ActionScript 2.0, musisz przeczytać tę część w całości, zwracając szczególną uwagę na informacje oznaczone jako „Nowa funkcja”.
30
ActionScript 3.0. Biblia
Po przeczytaniu części I i zdobyciu odrobiny praktyki nowicjusze w ActionScript 3.0 oraz czytelnicy z doświadczeniem powinni być w równym stopniu przygotowani do przygody z pozostałą częścią książki.
Część II: „Praca z obiektami ActionScript 3.0” W części II opisano najczęściej wykorzystywane struktury danych w ActionScript 3.0, sposób ich implementacji w ActionScript oraz to, co możesz z nimi zrobić. Opisane zostały następujące struktury danych:
Łańcuchy znaków.
Liczby, daty i funkcje matematyczne.
Tablice.
Obiekty.
XML.
Wyrażenia regularne.
Część III: „Praca z listą wyświetlania” W części III opisano podstawy konieczne do rozpoczęcia tworzenia programów posiadających aspekt wizualny. Fundamentalne informacje przedstawione tutaj będą często wykorzystywane w pozostałych częściach książki. Poznasz:
Listy wyświetlania używane przez program Flash Player do wyświetlania obiektów wizualnych.
Klasy wyświetlane używane do tworzenia wyświetlanych elementów oraz to, jak są od siebie zależne.
Wykorzystanie grafik utworzonych we Flashu lub zewnętrznych obrazów w projekcie ActionScript 3.0.
Drukowanie.
Wyświetlanie i interakcję z tekstem.
Część IV: „System obsługi zdarzeń” W części IV opisano podstawy pisania interaktywnego oprogramowania, wprowadzając pojęcie zdarzenia. Jest to kluczowy temat, który będzie wykorzystywany w wielu z pozostałych części. Dowiesz się:
Czym są zdarzenia, jak działają, jak ich używać.
Jak tworzyć własne zdarzenia.
O wykorzystaniu zdarzeń w celu reagowania na użycie myszy i klawiatury.
O wykorzystaniu klasy Timer i jej zdarzeń do kontrolowania ActionScript w czasie.
Wstęp
31
Część V: „Obsługa błędów” Część V da Ci narzędzia potrzebne do pisania programów ActionScript 3.0 solidnych jak skała, a w tym:
Wprowadzenie do obsługi błędów: klasa Error, bloki try oraz błędy asynchroniczne.
Zastosowanie debugera do znajdowania, wyjaśniania i eliminowania błędów w kodzie.
Strategie obsługi błędów.
Część VI: „Praca z danymi zewnętrznymi” W części VI poznasz sposoby wykorzystania danych ze źródeł znajdujących się poza programem, w tym:
Pobieranie danych i obrazów z internetu oraz nawigowanie do innych miejsc docelowych.
Komunikację z serwerem oraz kilka z licznych sposobów wysyłania danych tam i z powrotem.
Zapisywanie i pobieranie danych przechowywanych na komputerze użytkownika.
Wgrywanie i pobieranie plików.
Część VII: „Wzbogacanie programów dźwiękiem i filmem” W części VII wyjaśniono, jak dodawać do swoich programów elementy multimedialne. Zagadnienia to:
Wczytywanie, odtwarzanie i kontrolowanie dźwięków i muzyki.
Filmy — dostępne możliwości programu Flash Player, wczytywanie, streaming oraz odtwarzanie wideo.
Używanie wejścia z kamery lub mikrofonu użytkownika.
Część VIII: „Programowanie grafiki i ruchu” Z części VIII dowiesz się, jak generować w ActionScript 3.0 elementy wizualne i jak wprawić je w ruch. Poznasz między innymi:
Dodawanie filtrów oraz modyfikowanie kolorów istniejących grafik.
Rysowanie linii oraz kształtów.
Animowanie obiektów.
32
ActionScript 3.0. Biblia
Część IX: „Praca z danymi binarnymi” W części IX pokazano, jak zastosować kod ActionScript do binarnego odczytu, zapisu, przechowywania danych oraz komunikacji.
Część X: „Wdrażanie programu” Ta ostatnia część opisuje uruchamianie Twojej aplikacji w rzeczywistym świecie, w tym:
Umieszczanie programu na stronie internetowej w sposób zgodny ze standardami.
Komunikację z JavaScript w celu interakcji ze stroną internetową.
Komunikację między różnymi programami działającymi w tym samym czasie.
Konwencje i rozwiązania W trakcie czytania książki zauważysz wiele elementów organizacyjnych i typograficznych zaprojektowanych w celu umożliwienia Ci odbioru jak największej ilości informacji.
Wyróżnienia W książce napotkasz fragmenty wyróżnione z głównego tekstu. Takie specjalne uwagi stosujemy w celu zwrócenia Twojej uwagi na ważne informacje lub też w celu dodania kontekstu oraz opcjonalnych dodatków do głównego toku analizy. Ostrzeżenia zawierają informacje na temat zagadnień, na które trzeba uważać, zarówno zwykłych niedogodności, jak i potencjalnych zagrożeń dla Twoich danych lub systemów.
Wskazówki stosujemy zazwyczaj w celu wskazania Ci możliwości ułatwienia sobie pracy — podajemy w nich specjalne skróty klawiaturowe lub łatwiejsze sposoby wykonania jakiejś czynności. Uwagi zawierają dodatkowe, wspomagające informacje, które są pożyteczne, ale w pewien sposób wykraczają poza bieżący temat.
Te wyróżnienia dotyczą zagadnień w ActionScript 3.0, które różnią się od poprzednich wersji języka ActionScript. Jeśli potrafisz programować w innej wersji ActionScript, uwagi te pomogą Ci w przejściu do AS3. W rozdziałach opisujących klasy i rozwiązania zupełnie nowe w ActionScript 3.0 wyróżnienie to nie jest stosowane. Zamiast tego wprowadzenie do rozdziału poinformuje Cię o tym, że zagadnienia dalej w nim opisane są zupełnie nowe.
Wstęp
33
Formatowanie kodu oraz konwencje W niniejszej książce znajdziesz dużo kodów ActionScript. Tekst występujący w kodzie lub przeznaczony do zastosowania w kodzie jest prezentowany czcionką o stałej szerokości znaków, np. Object. Oprócz tego często dołączamy przykładowy kod, a robimy to na dwa sposoby: jako bloki przedstawiające całkowite przykłady oraz jako krótkie wycinki. Często zamieszczamy dłuższe przykłady, będące kompletnymi programami. Przykłady te mogą być uruchamiane bez pisania dodatkowego kodu, zgodnie z instrukcjami zawartymi w podrozdziale „Uruchamianie kodu zamieszczonego w książce” w dalszej części tego rozdziału. Przykłady te pokażą Ci kod realizujący rzeczywiste zagadnienia. Pomimo iż zagadnienia te są zazwyczaj dość proste, ma to na celu przedstawienie zastosowania danego zagadnienia oraz zademonstrowanie, jak dany kod ActionScript współpracuje z innym powiązanym kodem. Przykłady zawsze składają się z co najmniej jednej pełnej klasy oraz przynajmniej jednej klasy rozszerzającej klasę Sprite. Krótki przykład może wyglądać następująco: package pl.helion.as3biblia { import flash.display.Sprite; public class HelloWorld extends Sprite { public function HelloWorld() { trace("Witaj świecie!"); } } }
Z drugiej strony, fragmenty kodu są krótkimi blokami kodu, czasami nawet jednowierszowymi. Spotykać je będziesz w całej książce. Urywki nie posiadają kodu strukturalnego umożliwiającego Ci ich samodzielne uruchamianie i kompilowanie, ale służą raczej do pokazania wierszy kodu, które mogą zostać dodane do większego programu. Są bardzo zwięzłe i zawierają jedynie istotną treść, nie uwzględniając kontekstu. Tekst znajdujący się wokół takich fragmentów kodu wskaże Ci ich przeznaczenie. Ogólnie rzecz biorąc, zalecamy czytanie tych krótkich fragmentów kodu, ponieważ pomoże to w zrozumieniu danego zagadnienia. Czasami fragmenty kodu demonstrują działanie tego kodu poprzez wyprowadzenie niektórych wartości za pomocą funkcji trace(). Poszukaj efektu końcowego w panelu Console (konsola) programu Flex Builder lub w panelu Wyjście programu Flash. Komentarz do kodu za trace() zawiera oczekiwany rezultat. Przykładem fragmentu kodu może być: var a:int = 1; var b:int = 1; trace(a + b); // 2
Podczas czytania książki warto po prostu czytać takie fragmenty i próbować stosować je w dłuższych przykładach — modyfikować je tak, aby wykonywały inne czynności; testować je na okoliczność wystąpienia błędu; dodawać do nich kod, czyniąc je własnymi. Rozpoczęcie pisania kodu jest najlepszą metodą nauki.
34
ActionScript 3.0. Biblia
Witryna internetowa książki Uzupełnienie książki stanowi jej witryna internetowa pod adresem http://helion.pl/ksiazki/ acs3bi.htm. Znajdziesz tutaj między innymi przykładowe kody zamieszczone w książce oraz, o ile to będzie konieczne, erratę do książki. Pliki z kodami zamieszczonymi w książce możesz również pobrać bezpośrednio z serwera FTP wydawnictwa Helion, pod adresem ftp://ftp.helion.pl/przyklady/acs3bi.zip.
Minimalne wymagania Możesz przeczytać całą książkę bez dotknięcia komputera, jednak zalecamy tworzenie swoich własnych przykładów oraz praktyczne wykorzystywanie co jakiś czas tego, czego się tutaj dowiesz. Do uruchamiania kodu ActionScript 3.0 będziesz potrzebował jednego z poniższych programów:
Adobe Flex Builder 2 (lub nowszy) (Mac OS X 10.4.7 lub nowszy, Windows XP SP2, Windows 2000 Server Professional, Windows Server 2003 lub nowszy).
Adobe Flash CS3 (Mac OS X 10.4.8 lub nowszy, Windows XP SP2, Windows Vista lub nowszy).
Adobe Flex 2 (lub nowszy) Software Development Kit (SDK) (Mac OS X 10.4.x, Windows 2000, XP, Server 2003, Red Hat Enterprise Linux 3, SUSE 10, Solaris 9; lub nowsze).
W czasie, gdy niniejsza książka powstaje, Flex Builder 3 oraz Flash CS3 są dostępne jako 30-dniowe wersje próbne oraz wersje komercyjne. Flex 3.1 SDK jest dostępny za darmo. Oprócz tego Adobe zapowiedziało uczynienie pod koniec roku 2007 Flex 2 SDK projektem open source. Więcej informacji na temat tej zapowiedzi znajdziesz na stronie internetowej http://labs.adobe.com/wiki/index.php/Flex:Open_Source:FAQ. Do uruchamiania swoich dzieł będziesz potrzebował programu Flash Player 9 lub w nowszej wersji, dostępnego za darmo dla systemów Windows, Mac OS X, Linux oraz Solaris. Pod podanymi poniżej adresami stron internetowych znajdziesz więcej informacji na temat wymagań sprzętowych oraz oprogramowania: www.adobe.com/products/flex/productinfo/systemreqs/ www.adobe.com/products/flash/systemreqs/ www.adobe.com/products/flashplayer/productinfo/systemreqs/
Uruchamianie kodu zamieszczonego w książce W podrozdziale wyjaśniono krok po kroku, jak uruchamiać przykłady zamieszczane w tej książce, co umożliwi Ci przeprowadzanie na nich różnorakich eksperymentów. Kroki, jakie będziesz musiał wykonać w celu uruchomienia zawartych tutaj przykładów, będą
Wstęp
35
zależne od narzędzia, z którego korzystasz. Przedstawiamy tutaj odpowiednie procedury dla programów Adobe Flex Builder 3 oraz Adobe Flash CS3 Professional. Przyjrzyjmy się najpierw sposobowi uruchamiania całego przykładu. Będziesz mógł z łatwością rozpoznać przykłady, ponieważ są one kompletnymi klasami rozszerzającymi klasę Sprite (więcej na temat klas napisano w rozdziale 3.). Mogą też zawierać więcej niż jedną klasę. Powiedzmy, że chcemy uruchomić następujący przykład: package pl.helion.as3biblia { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFormat; public class Example extends Sprite { public function Example() { var tf:TextField = new TextField(); tf.multiline = true; tf.wordWrap = true; tf.width = stage.stageWidth; tf.height = stage.stageHeight; tf.text = "Gratulacje! Właśnie " + "uruchomiłeś przykład z ActionScript 3.0. Biblia!"; var format:TextFormat = new TextFormat(); format.font = "_sans"; format.size = 42; tf.setTextFormat(format); addChild(tf); trace("Gotowe!"); } } }
Adobe Flash CS3 Professional Pierwszą czynnością będzie utworzenie na dysku twardym folderu, w którym będą przechowywane i uruchamiane przykłady. Następne kroki odbywają się wewnątrz tego folderu, niezależnie od tego, gdzie go umieścisz. Możesz pisać kod lub pobrać go ze strony internetowej utworzonej dla tej książki. Kody dłuższych przykładów będą dostępne do pobrania zgodnie z opisem z podrozdziału „Witryna internetowa książki”. Jeśli już posiadasz te pliki, umieść je w utworzonym folderze lub prześledź następujące kroki, prowadzące do utworzenia tego pliku od nowa: 1. Uruchom program Flash i utwórz nowy plik ActionScript, wybierając Plik/Nowy, a następnie Plik ActionScript, jak pokazano na rysunku W.1. Przepisz lub wklej zawartość przykładu w edytorze, który otwarty zostanie przez Flasha. 2. Zapisz plik w ścieżce określonej przez nazwę klasy (więcej na ten temat dowiesz się z rozdziału 3.). W tym przypadku klasa to pl.helion.as3biblia.Example, dlatego umieść plik w utworzonym folderze w podfolderach pl/helion/as3biblia/ i nazwij go Example.as. Do utworzenia pośrednich folderów możesz użyć systemu
36
ActionScript 3.0. Biblia
Rysunek W.1. Utwórz nowy plik ActionScript
operacyjnego lub okna dialogowego Zapisz jako. Poprawna nazwa i lokalizacja klasy są zasadniczymi elementami uruchamiania kodu. Zwróć uwagę na to, że na rysunku W.2 plik jest umieszczany w poprawnej ścieżce. Musisz powtórzyć ten krok dla każdego pliku, jeśli przykład zawiera wiele plików klas. 3. Aby utworzyć nowy plik Flash, służący do uruchamiania kodu, skorzystaj ponownie z polecenia Plik/Nowy. Tym razem wybierz jednak opcję Plik Flash (ActionScript 3.0), jak na rysunku W.3. 4. Zanim zrobisz cokolwiek, zapisz ten plik w folderze utworzonym dla tego przykładu. Nie umieszczaj go w żadnym z podfolderów, jak chociażby pl/. Możesz nadać mu dowolną nazwę. Na rysunku W.4 plik ten został nazwany ExampleRunner.fla. Zapisując plik w odpowiedniej lokalizacji, gwarantujesz, że będzie on mógł odnaleźć potrzebne mu klasy. 5. Każdy przykład dysponuje klasą główną. Jeśli przykład jest pojedynczą klasą, klasa główna obejmuje cały przykład. W przeciwnym razie znajduje się w listingu jako pierwsza. Klasa główna zawsze rozszerza klasę Sprite.
Wstęp
37
Rysunek W.2. Zapisz plik ActionScript, używając poprawnej nazwy
Gdy już zorientujesz się, która klasa w przykładzie jest klasą główną, wprowadź pełną nazwę klasy (patrz rozdział 3.) do pola Klasa dokumentu w panelu Właściwości. Jeśli panel ten nie jest widoczny, możesz go wyświetlić, wybierając z menu Okno/Właściwości/Właściwości. W tym przykładzie klasą główną lub klasą dokumentu jest pl.helion.as3biblia.Example, której nazwa wprowadzona została na rysunku W.5. Jeśli pliki Flash oraz plik ActionScript nie zostały zapisane w odpowiednich lokalizacjach z wykorzystaniem poprawnych nazw, zobaczysz ostrzeżenie o następującej treści: „Definicja dla klasy dokumentu nie została odnaleziona w ścieżce klasy, w związku z tym zostanie ona automatycznie wygenerowana w pliku SWF podczas eksportu”. Jeśli widzisz to ostrzeżenie, upewnij się, czy wszystkie otwarte pliki zostały zapisane oraz czy zapisane zostały w odpowiedniej lokalizacji. Klasa dokumentu we Flashu jest klasą powiązaną ze sceną podczas działania pliku SWF. Cokolwiek będzie się znajdować na scenie, stanie się częścią tej klasy, a wszystko, co zrobisz z tą klasą, będzie miało wpływ na scenę. Po dodaniu w klasie Example obiektu TextField do sceny dodane zostanie pole tekstowe, ponieważ scena jest teraz instancją klasy Example. Więcej na temat listy wyświetlania dowiesz się z części III.
38
ActionScript 3.0. Biblia
Rysunek W.3. Utwórz nowy plik Flash
Konfiguracja tego przykładu dobiegła końca. Aby zobaczyć efekt działania przykładowego kodu, wybierz Sterowanie/Testuj film. Efekt działania klasy Example możesz zobaczyć na rysunku W.6. Wartość zwracana przez funkcję trace() widoczna jest w panelu Wyjście. Wiele przykładów w tej książce będzie wyprowadzać informacje tylko w ten sposób, dlatego sprawdzaj zawsze panel Wyjście, jeśli nie będziesz widział niczego podczas testowania filmu. Po skonfigurowaniu przykładu możesz modyfikować pliki ActionScript we Flashu, zapisywać zmiany i ponownie uruchamiać polecenie Testuj film w celu testowania swojego kodu.
Uruchamianie fragmentu kodu Możesz też chcieć uruchamiać krótkie fragmenty kodu zamieszczane w tej książce. Wiele takich wycinków ma jedynie funkcję objaśniającą i nie będzie możliwe ich samodzielne uruchomienie, jednak możesz samodzielnie uzupełnić brakujące polecenia i uruchomić taki kod, lub utworzyć własny kod wykorzystujący zamieszczony przez nas wycinek. Inne wycinki uruchamiać się będą bez dodatkowych modyfikacji. Przedstawione poniżej kroki umożliwią Ci uruchomienie bloków kodu niezawartych w klasie.
Wstęp
39
Rysunek W.4. Zapisz plik Flash
1. Utwórz nowy Plik Flash (ActionScript 3.0), zgodnie z tym, co pokazano wcześniej na rysunku W.3. Nie musisz zapisywać go w jakimś konkretnym miejscu. 2. Zaznacz pierwszą klatkę na osi czasu. Jeśli nie widzisz osi czasu, wybierz Okno/Oś czasu z menu. Wprowadź w panelu Operacje kod, który chcesz uruchomić. Na rysunku W.7 przedstawione zostało miejsce przeznaczone do wprowadzania kodu. Upewnij się, że panel Operacje powiązany jest z pierwszą klatką warstwy. Na rysunku W.7 warstwa nazwana została Warstwa 1, dlatego zakładka w panelu Operacje ma nazwę Warstwa 1:1, a w pierwszej klatce na osi czasu pojawi się mała litera a. Jeśli spróbujesz umieścić kod gdziekolwiek indziej, może to spowodować wystąpienie błędu. W wielu przypadkach kod potrzebny do uruchomienia takiego wycinka kodu został pominięty. Dlatego podczas prób uruchamiania przykładowych fragmentów będziesz musiał dodać wyrażenia importujące lub inne elementy kodu.
40
ActionScript 3.0. Biblia
Rysunek W.5. Ustaw klasę dokumentu
Adobe Flex Builder 3 Przykłady zamieszczone w tej książce możesz też uruchamiać za pomocą programu Adobe Flex Builder 3 lub jego nowszej wersji. Możesz przepisać przykłady z książki lub skopiować je ze strony internetowej utworzonej dla tej książki. Pierwszą rzeczą, jaką powinieneś zrobić, jest utworzenie nowego projektu dla tego przykładu. 1. Uruchom program Flex Builder i utwórz nowy projekt ActionScript, wybierając File/New/ActionScript Project (plik/nowy/projekt ActionScript). Podążaj za krokami kreatora i nazwij projekt w dowolny sposób. Pomoże Ci to w nazwaniu projektu w sposób odmienny od nazwy klasy głównej. Na rysunku W.8 utworzyliśmy projekt o nazwie ExampleProject. Tym razem kreator utworzył za nas klasę główną o nazwie ExampleProject. Flex Builder „lubi” mieć główną klasę lub aplikację w domyślnym pakiecie. (W rozdziale 3. znajdziesz więcej informacji na temat pakietów). Z tego też
Wstęp
41
Rysunek W.6. Przykład w akcji
powodu, jeśli uruchamiany przykład znajduje się w domyślnym pakiecie, możesz pominąć następne kroki, wybierając nazwę przykładowej klasy jako nazwę projektu i zastępując pustą klasę stworzoną dla Ciebie przez kreatora klasą główną przykładu. 2. Jeśli klasa główna przykładu znajduje się w innym pakiecie, np. pl.helion.as3biblia, utwórz nowy plik ActionScript, wybierając File/New/ActionScript Class (plik/nowy/klasa ActionScript) i wprowadź odpowiednie dane w kreatorze. Na rysunku W.9 możesz zobaczyć okno kreatora New ActionScript Class. Nazwa pakietu to pl.helion.as3biblia, nazwa klasy to Example, a rozszerza ona klasę flash.display.Sprite. Teraz możesz przejść do wypełnienia szkieletu utworzonego przez kreatora klasy. Jeśli masz już te pliki, możesz umieścić je w folderze projektu w poprawnej lokalizacji. Powinny się pojawić po tym w widoku Flex Navigator (nawigator Flex) programu Flex Builder. Klasa Example nie znajduje się w domyślnym pakiecie, dlatego też Flex Builder nie pozwoli na jej bezpośrednie uruchomienie. Aby szybko ominąć ten problem, po prostu dodaj wiersz: addChild(new Example());
42
ActionScript 3.0. Biblia
Rysunek W.7. Wprowadzanie fragmentu kodu do operacji klatki
do konstruktora klasy aplikacji, w tym przypadku ExampleProject. Dodatkowo ważne jest, aby zaimportować klasę Example z pakietu pl.helion.as3biblia, a jeśli użyjesz automatycznego uzupełniania dla słowa Example, import powinien zostać zrealizowany automatycznie. Jeśli przykład zawarty jest w domyślnym pakiecie, po prostu użyj klasy aplikacji jako klasy głównej, a nie będziesz musiał dodawać kolejnego pliku lub tego kodu. Aby uruchomić aplikację w programie Flex Builder, rozpocznij nową sesję debugowania. Dzięki temu będziesz widzieć wartości zwracane przez instrukcje trace() zamieszczone w przykładzie, jeśli takowe występują. Nową sesję debugowania możesz rozpocząć, wybierając Run/Debug ExampleProject (uruchom/debugowanie ExampleProject) lub jeśli inaczej nazwałeś swój projekt, wybierz odpowiadające mu polecenie. Flex Builder uruchomi domyślnie przykład w Twojej przeglądarce internetowej. Jeśli chcesz debugować ten przykład, musisz mieć zainstalowaną wersję debugującą programu Flash Player w przeglądarce, której Flex Builder używa. Możesz też uruchomić tę aplikację
Wstęp
43
Rysunek W.8. Nowy projekt ActionScript utworzony za pomocą kreatora New Project w programie Flex Builder
w programie Flash Player. Otwórz właściwości projektu poprzez kliknięcie prawym przyciskiem myszy na nazwie projektu w panelu Flex Navigator lub wybierając z menu Project/Properties (projekt/właściwości). Wybierz sekcję ActionScript Compiler (kompilator ActionScript), usuń zaznaczenie Generate HTML wrapper file (generuj plik otaczający HTML) i kliknij OK. Po wykonaniu tych czynności Flex Builder uruchomi Twoją aplikację w działającym samodzielnie programie Flash Player.
Więcej informacji Czynności opisane w tym podrozdziale miały na celu jedynie przybliżenie Ci edycji i uruchamiania kodu, a zaprezentowane zostały po to, aby pomóc Ci uruchamiać przykłady zamieszczone w tej książce. Więcej informacji na temat korzystania z programów Flex Builder 3, Adobe Flash CS3 Professional lub ich nowszych wersji znajdziesz w dokumentacjach dostarczanych razem z tymi produktami oraz w książkach, które polecaliśmy wcześniej.
44
ActionScript 3.0. Biblia
Rysunek W.9. Tworzenie klasy Example
Co dalej Uzbrojony w tę wiedzę powinieneś być odpowiednio przygotowany do programowania w ActionScript 3.0. Podczas implementowania pomysłów w kodzie otwierać się będą przed Tobą niezliczone drzwi i nie mamy tak naprawdę możliwości wskazania Ci, do czego może się przydać Twoja nowo zdobyta wiedza. Zależy to wyłącznie od Ciebie. Zachęcamy Cię do pracy nad projektami odpowiadającymi Twoim zainteresowaniom. Najwięcej nauczysz się poprzez pracę, a najlepiej pracuje się wtedy, gdy robi się to, co się lubi. Wymarz sobie jakąś aplikację, animację, symulację czy instalację i uczyń ją rzeczywistością. Znajdź kilka zleceń, które będą wydawać Ci się interesujące, i powalcz o nie. Zmierz się z wyzwaniem, a pod ręką miej zawsze książkę ActionScript 3.0. Biblia, która pomoże Ci, gdy będziesz tego potrzebował. Kiedy już nabędziesz wprawy w programowaniu w ActionScript 3.0, przekaż tę książkę kolejnemu uczniowi AS3, a Ty kontynuuj swoją naukę. Zakres zastosowania programowania jest cudownie szeroki. Możesz zagłębiać się dalej: poznawać różne algorytmy, struktury danych, projektowanie obiektowe, wzorce projektowe, architekturę lub praktyki tworzenia oprogramowania. Możesz ulepszać swoje umiejętności ActionScript 3.0: poznać Flex lub AIR. Możesz czynić rzeczy realistycznymi: poznać techniki grafiki komputerowej, fizyki, animacji lub sztuki algorytmicznej. Możesz dowiedzieć się więcej na temat programowania, ucząc się innego języka programowania. Możesz skoncentrować się na komunikacji serwerowej. Możliwości rozwoju są nieograniczone.
Część I
Rozpoczęcie pracy z ActionScript 3.0 W tej części: Rozdział 1. Wprowadzenie do ActionScript 3.0 Rozdział 2. Podstawy języka ActionScript 3.0 Rozdział 3. Programowanie z wykorzystaniem klas Rozdział 4. Praca z metodami i funkcjami Rozdział 5. Walidacja programów
46
Część I Rozpoczęcie pracy z ActionScript 3.0
Rozdział 1. Wprowadzenie do ActionScript 3.0
47
Rozdział 1.
Wprowadzenie do ActionScript 3.0 W tym rozdziale:
Wstęp do ActionScript 3.0
Do czego używać ActionScript 3.0
Nowe funkcje w ActionScript 3.0
Osobom pracującym wcześniej z ActionScript 2.0 ActionScript 3.0 może się wydać programem zupełnie nowym, a jednocześnie znajomym. Jest nowy, ponieważ został utworzony od podstaw z wykorzystaniem nowej wersji specyfikacji ECMA i uruchamiany jest w nowej maszynie wirtualnej (AVM2). Pomijając jednak wszelkie nowości ActionScript 3.0, język ten jest na tyle podobny do ActionScript 2.0, że użytkownicy pracujący wcześniej we Flashu odnajdą się w tej wersji bez większych trudności. W tym rozdziale przyjrzymy się, czym tak właściwie jest ActionScript, do czego możesz go zastosować oraz jakie są nowe funkcje języka ActionScript 3.0.
Czym jest ActionScript? Czytając tę książkę, masz prawdopodobnie ogólne pojęcie o języku ActionScript, jednak dalsze objaśnienia pomogą Ci lepiej zrozumieć proces tworzenia zawartości za pomocą ActionScript. Ogólnie rzecz biorąc, ActionScript jest językiem programowania wykorzystywanym do tworzenia treści dla programu Flash Player. Możesz użyć narzędzi takich jak Flash CS3 Professional czy Flex Builder do tworzenia treści z wykorzystaniem innych narzędzi lub technologii, takich jak narzędzia rysujące, symbole z biblioteki, osie czasu czy MXML. ActionScript może być używany jako uzupełnienie tych narzędzi, ale może też całkowicie je zastąpić. ActionScript jest niezbędny wszędzie tam, gdzie chcesz tworzyć dynamiczne aplikacje Flash reagujące na działanie użytkownika i dające się łatwo dostosować. Oto kilka wybranych zagadnień, jakie możesz zrealizować za pomocą języka ActionScript:
48
Część I Rozpoczęcie pracy z ActionScript 3.0
Wczytywanie obrazów.
Odtwarzanie dźwięków i filmów.
Rysowanie poprzez programowanie.
Wczytywanie danych takich jak XML.
Reagowanie na działania użytkownika, takie jak kliknięcia myszą.
Do czego używać ActionScript 3.0? W chwili pisania tej książki popularne są dwa podstawowe sposoby pracy z ActionScript 3.0: tworzenie aplikacji Flash za pomocą programu Flash (Flash CS3) lub tworzenie aplikacji Flex albo ActionScript w programie Flex Builder 3. Nie ma ściśle określonej reguły, która mówiłaby, dlaczego należy użyć jednego zestawu narzędzi zamiast drugiego. Występuje raczej szeroki zakres zazębiania się tych dwóch aplikacji. Wybierając zestaw narzędzi, którego będziesz używać, powinieneś uzmysłowić sobie, do jakiego sposobu pracy jesteś bardziej przyzwyczajony. Jeśli wolisz używać narzędzi do rysowania oraz osi czasu, prawdopodobnie najlepszą opcją będzie dla Ciebie Flash CS3. Z kolei program Flex Builder jest lepszy dla tych, którzy czują się pewniej, pisząc programy w środowiskach do tworzenia oprogramowania, a mniej pewnie czują się podczas tworzenia animacji w aplikacji Flash CS3. Nie ma zatem tak naprawdę powodu, dla którego nie mógłbyś używać obu zestawów narzędzi do tworzenia aplikacji. Dobrą nowiną jest w tym przypadku fakt, że niezależnie od wybranego zestawu narzędzi ActionScript 3.0 pozostanie taki sam. Pisząc tę książkę, staraliśmy się tak dobierać przykłady, aby możliwe było ich użycie zarówno w programie Flash CS3, jak i w Flex Builder. Jeśli używasz Flex Builder 3 (w książce tej nie będziemy omawiać frameworku Flex) do tworzenia aplikacji opartych na ActionScript, zmuszony będziesz do ścisłego projektowania obiektowego, co oznacza, że podstawą Twojej aplikacji musi być klasa (więcej informacji na temat klas znajdziesz w rozdziale 3.). W przypadku Flasha CS3 tak nie jest. Gdy używasz Flash CS3, masz możliwość umieszczania swojego kodu ActionScript w klatkach kluczowych na osi czasu. Nie jest to wprawdzie błędem, może jednak powodować problemy, jeśli chcesz tworzyć dobry kod z uwzględnieniem najlepszych praktyk, nadający się do ponownego użycia. Z tego powodu w książce zachęcamy do stosowania podczas pracy we Flashu CS3 klasy dokumentu. Używana jest też we wszystkich przykładach zamieszczonych w książce. Klasa dokumentu jest we Flashu CS3 odpowiednikiem klasy głównej w projekcie Flex Builder 3. Używając klasy dokumentu, poznasz najbardziej skalowalny sposób pisania kodu ActionScript 3.0 we Flashu CS3, a to, czego się nauczysz, możesz w każdej chwili łatwo przełożyć na kod Flex Buildera 3.
Co nowego w ActionScript 3.0? Jak już wspomnieliśmy wcześniej, ActionScript 3.0 posiada liczną grupę nowych funkcji. Poznasz je wszystkie szczegółowo w następnych rozdziałach, ale tutaj zamieszczamy przegląd najważniejszych spośród nich.
Rozdział 1. Wprowadzenie do ActionScript 3.0
49
Lista wyświetlania W ActionScript 2.0 występowały trzy główne typy obiektów, które mogły być wyświetlane: klipy filmowe, przyciski oraz pola tekstowe. Obiekty tych typów nie dziedziczyły ze wspólnego źródła, co oznacza, że nie dotyczył ich polimorfizm. Oprócz tego instancje tych typów wyświetlanych zawsze były w stałym związku rodzic – potomek z innymi instancjami. Aby utworzyć np. nowy klip filmowy, trzeba było utworzyć go jako potomka istniejącego klipu filmowego. Nie było możliwości przeniesienia klipu z jednego rodzica do drugiego. W ActionScript 3.0 występuje wiele nowych typów wyświetlanych. Oprócz znanych typów, takich jak klip filmowy, przycisk i pole tekstowe, masz teraz do dyspozycji kształty, klasę Sprite, klasę Loader, bitmapy i inne. Wszystkie typy wyświetlane w ActionScript 3.0 dziedziczą z klasy flash.display.DisplayObject, co oznacza, że możesz używać polimorfizmu. Co więcej, w ActionScript 3.0 obiekty typów wyświetlanych można tworzyć niezależnie od innych obiektów tego typu , a nawet umieszczać je w jednym obiekcie rodzica. Innymi słowy, możesz utworzyć pole tekstowe w ActionScript 3.0, po prostu wywołując konstruktor jako element instrukcji new, a pole to będzie istnieć niezależnie od obiektu rodzica. var text:TextField = new TextField();
Możesz dodać to pole tekstowe do kontenera rodzica w dowolnym momencie. Następujący przykład ilustruje to za pomocą obiektu wyświetlanego nazwanego container, który może być obiektem typu Sprite lub obiektem innego typu wyświetlanego. container.addChild(text);
W powyższym przykładzie nazwa container została użyta jako ogólna nazwa zmiennej, która przypuszczalnie jest referencją do obiektu utworzonego w innym miejscu w kodzie.
Hierarchia kontenerów rodziców oraz ich potomków zwana jest w ActionScript 3.0 listą wyświetlania.
Błędy czasu wykonywania ActionScript 3.0 umożliwia obsługę wielu nowych błędów czasu wykonywania. Jest to bardzo istotna nowa funkcja, ponieważ pozwala na o wiele szybsze diagnozowanie problemów. W ActionScript 2.0, gdy błąd wystąpił podczas pracy, często występował „po cichu” i dla Ciebie jako programisty trudne było odnalezienie jego przyczyny. Dzięki ulepszonym procedurom obsługi błędów oraz raportowaniu błędów debugowanie aplikacji ActionScript 3.0 jest teraz o wiele prostsze niż w ActionScript 2.0.
Kontrola typów danych w czasie wykonywania Kontrola typów w ActionScript 2.0 była realizowana tylko przez kompilator, a nie podczas wykonywania programu. Podczas wykonywania wszystkie typy ActionScript 2.0 są dynamiczne. W ActionScript 3.0 kontrola typów realizowana jest w czasie wykonywania programu (kontrola typów w czasie kompilacji może być włączona albo wyłączona,
50
Część I Rozpoczęcie pracy z ActionScript 3.0
w zależności od trybu kompilacji — kontrola typów przeprowadzana jest podczas kompilacji w trybie ścisłym). Zaleta takiego rozwiązania polega na tym, że teraz błędnie dopasowane dane raportowane są jako błąd, a oprócz tego dzięki zachowywaniu typów w czasie wykonywania ulepszona zostaje wydajność aplikacji oraz zarządzanie pamięcią.
Domknięcia metod W ActionScript 3.0 wszystkie metody posiadają właściwe domknięcie dzięki wbudowanej delegacji zdarzeń, tak że odwołanie do metody zawsze zawiera obiekt, z którego wystąpiło pierwotne wywołanie metody. Ma to znaczenie dla zarządzania zdarzeniami i różni się znacząco od domknięcia metod w ActionScript 2.0. W przypadku ActionScript 2.0 po odwołaniu się do metody obiekt wywołujący daną metodę nie jest utrzymywany. Prowadzi to do powstawania problemów, zwłaszcza podczas dodawania obiektów nasłuchujących. W ActionScript 2.0 jako rozwiązanie często stosuje się delegaty. W ActionScript 3.0 stosowanie delegatów nie jest konieczne.
Wewnętrzny model zdarzeń Model zdarzeń w ActionScript 3.0 jest wbudowany w rdzeń języka. Klasa flash.events. EventDispatcher jest bazową klasą dla wielu natywnych klas ActionScript, w tym wszystkich typów obiektów wyświetlanych. Oznacza to, że występuje jeden standardowy sposób wysyłania i obsługi zdarzeń.
Wyrażenia regularne Wyrażenia regularne są wspaniałym sposobem odnajdywania podciągów pasujących do wzorca. Pomimo tego, że wyrażenia regularne już od dawna wbudowane są w siostrzane języki, jak chociażby JavaScript, nigdy wcześniej nie były one częścią języka ActionScript. ActionScript 3.0 zawiera wewnętrzną klasę RegExp, która umożliwia uruchamianie wyrażeń regularnych w programie Flash Player.
E4X E4X (skrót od ECMAScript for XML) jest nowym sposobem pracy z danymi XML w ActionScript. Chociaż możliwa jest w dalszym ciągu praca z XML w sposób znany z ActionScript 2.0, a polegający na poruszaniu się po strukturze opisywanej przez model DOM, E4X pozwala Ci pracować z XML w sposób o wiele bardziej naturalny i intuicyjny.
Rozdział 1. Wprowadzenie do ActionScript 3.0
51
Podsumowanie
ActionScript 3.0 jest nowym językiem, wystarczająco podobnym do ActionScript 2.0, aby jego nauka nie okazała się zbyt trudna.
Możesz używać ActionScript 3.0, tak jak używałeś ActionScript 2.0 (w klasach lub na osi czasu), jednak preferowanym zastosowaniem ActionScript 3.0 jest używanie go w klasach przy zastosowaniu zasad programowania obiektowego.
ActionScript 3.0 wprowadza wiele nowych funkcji, w tym nowy sposób zarządzania typami wyświetlanymi, obsługę błędów czasu wykonywania, kontrolę typów danych w czasie wykonywania, zasięg metod, wewnętrzny model zdarzeń, wyrażenia regularne oraz nowy sposób pracy z XML.
52
Część I Rozpoczęcie pracy z ActionScript 3.0
Rozdział 2.
Podstawy języka ActionScript 3.0 W tym rozdziale:
Definiowanie zmiennych i stałych
Stosowanie operatorów i funkcji
Przypisywanie rdzennych typów danych
Praca z zasięgami
Techniki efektywnego komentowania kodu
Chcesz więc zostać programistą ActionScript? Ten rozdział pomoże Ci rozpocząć naukę poprzez przedstawienie podstawowej składni i struktury tego języka. Jeśli programowałeś wcześniej w innych językach, niektóre poruszane tutaj tematy będą Ci znajome. Jednak nawet jeśli jesteś doświadczonym programistą ActionScript 2.0, w nowej wersji języka znajdziesz kilka istotnych różnic, dlatego zalecamy Ci lekturę tego rozdziału.
Stosowanie zmiennych W szkole średniej często kłóciłem się z nauczycielem algebry, że wiedza, którą przekazuje, nigdy nie okaże się przydatna w rzeczywistym świecie. Pomimo tego, iż do tej pory nigdy poza salą lekcyjną nie musiałem upraszczać żadnego wielomianu, muszę przyznać, że się myliłem. ActionScript 3.0 jest całkowicie zbudowany w oparciu o zmienne, takie jak zmienne x oraz y występujące w zadaniu algebraicznym. Zmienna jest reprezentacją liczby, łańcucha znaków lub innej wartości, która może zmieniać wartości w sposób podobny do tego, w jaki zmienna x reprezentuje liczbę o dowolnej wartości. Możesz traktować zmienne jak kontenery służące do przechowywania informacji. W ActionScript zmienne przechowują fragmenty informacji, znane jako obiekty, w czasie wykonywania Twojego programu. Umożliwiają programowi tymczasowe przechowywanie tej informacji i odmienne działanie w zależności od tej informacji. Zmienne są używane w prawie każdym aspekcie programowania. Możesz też spotkać się ze zmiennymi, do których odwołuje się jak do właściwości, gdy są one częścią klasy.
54
Część I Rozpoczęcie pracy z ActionScript 3.0
Anatomia deklaracji zmiennej Definiowanie własnych zmiennych jest proste. Przyjrzyjmy się typowej deklaracji zmiennej i omówmy jej kolejne części w poniższej liście: var food:String; food = "pizza";
— wszystkie zmienne deklarowane są za pomocą słowa kluczowego var, po którym występuje nazwa zmiennej. W poprzednich wersjach ActionScript słowo var było opcjonalne.
var
— to jest nazwa zmiennej. Może być dowolnym wyrazem lub łańcuchem znaków i liczb, rozpoczynającym się albo od litery, albo od podkreślenia. Zalecamy nazywanie swoich zmiennych za pomocą opisowych wyrazów z wykorzystaniem notacji wielbłądziej, rozpoczynając od małej litery. Np. mainCourse = "pizza";.
food
— definiuje typ danych lub typ informacji, jakie mogą być umieszczane w danej zmiennej. Słowo String może być zastąpione dowolną nazwą klasy lub interfejsu. Typy zmiennych omówimy w dalszej części tego rozdziału.
:String
food = "pizza"; — to wyrażenie ustawia wartość zmiennej food
na słowo „pizza”, wykorzystując w tym celu operator = (przypisanie). Cudzysłowy obok słowa „pizza” wskazują, że jest to tekst, a nie zmienna o nazwie pizza.
Deklaracja zmiennej i przypisanie jej wartości może również zostać zrealizowane w jednym wierszu: var drink:String = "Piwo korzenne";
Możesz nawet deklarować wiele zmiennych w jednym wierszu: var breakfast:String, lunch:String, dinner:String;
Po zdefiniowaniu zmiennej może zostać ona wykorzystana do zastąpienia wystąpień jej wartości. Na przykład: trace("Moja ulubiona potrawa to " + food); // wyświetlone zostanie "Moja ulubiona potrawa to pizza"
Stałe w tym zmieniającym się świecie ActionScript 3.0 wprowadza specjalny typ zmiennej nazywany stałą. Stałe definiują wartości niezmieniające się podczas wykonywania programu. Jest to dobre rozwiązanie dla wartości nigdy się niezmieniających albo mających być chronionymi przed ewentualną zmianą. Jeśli chcesz np. zachować temperaturę wrzenia wody w stopniach Celsjusza, możesz napisać następujący kod: const BOILING_POINT:int = 100;
Jak widać, bardzo przypomina to deklarację zmiennej, jednak słowo var zostało zastąpione słowem kluczowym const. Stałe mogą być definiowane jedynie na początku klasy razem z innymi właściwościami lub w konstruktorze klasy. Próba przypisania nowej wartości do stałej w dowolnym miejscu kodu będzie skutkować błędem.
Rozdział 2. Podstawy języka ActionScript 3.0
55
Stałe są używane często w API ActionScript 3.0, zwłaszcza w pakietach flash.errors oraz flash.events. Przyjęło się, że nazwy stałych pisze się wersalikami, a poszczególne wyrazy oddzielane są znakiem podkreślenia.
Stosowanie literałów Oprócz zmiennych i wartości zwracanych przez funkcje będziesz w swoim kodzie używać jeszcze literałów. Pojęcie literału odwołuje się do każdej wartości dołączonej do kodu w sposób jawny w chwili jego kompilacji. Literały zapewniają dużą wygodę podczas posługiwania się złożonymi typami danych. Poniżej przedstawiamy listę różnych typów wartości, które możesz dołączać bezpośrednio do kodu:
Wartości liczbowe (w tym int i uint), np. 42, –100, 98.6.
Wartości logiczne true oraz false.
Łańcuchy znaków pisane z pojedynczymi lub podwójnymi cudzysłowami, np. "Lorem ipsum".
Specjalne puste wartości void, null oraz undefined.
Wartości tablicowe używające nawiasów prostokątnych, np. ["Poniedziałek", "Wtorek", "Środa"].
Ogólne obiekty definiowane za pomocą składni używającej nawiasów klamrowych, np. {name: "Alita", likesSpaghetti:true}.
ActionScript 3.0 wprowadza dwa nowe literały, które mogą być dołączane bezpośrednio do kodu w celu definiowania wyrażeń regularnych oraz obiektów XML:
Wyrażenia regularne zamknięte w ukośnikach, np. /href="(.*)"/. Elementy pisane w XML, jak chociażby .
XML oraz wyrażenia regularne opisane zostaną odpowiednio w rozdziałach 10. oraz 11. Zalecamy unikanie częstego stosowania literałów w kodzie, ponieważ mogą być trudne do śledzenia i zamiany. Zamiast tego zdefiniuj i użyj zmiennej lub stałej z wartością literału.
Stosowanie kontroli dostępu Pracując z klasami, będziesz używać dodatkowego wyrazu pisanego przed deklaracją zmiennej (lub funkcji), np.: private var lunchtime:Date;
Wyraz ten nazywa się modyfikatorem dostępu. Określa on, czy zmienna lub funkcja będzie dostępna dla klas innych niż ta, w której obiekt jest zdefiniowany. Więcej informacji o klasach oraz ich znaczeniu znajdziesz w dalszej części książki.
56
Część I Rozpoczęcie pracy z ActionScript 3.0
W ActionScript 3.0 występują cztery słowa kluczowe służące do definiowania dostępu. Oto one: — określa, że zmienna lub metoda jest dostępna dla wszystkich klas próbujących uzyskać do niej dostęp. Oznacza to, że każdy kod może uzyskać dostęp do zmiennej lub wywołać metodę oznaczoną tym słowem kluczowym.
public
— prywatne zmienne i metody mogą być używane jedynie przez klasę, w której są zdefiniowane. Próba uzyskania dostępu do takich metod lub zmiennych spoza klasy, w której są zdefiniowane, spowoduje wystąpienie błędu podczas kompilacji. Dotyczy to także klas potomnych. W przeciwieństwie do ActionScript 2.0 klasy potomne nie mają dostępu do prywatnych członków klasy. W takim przypadku należy użyć słowa kluczowego protected, które opiszę niżej.
private
— obiekty wewnętrzne nie są ani publiczne, ani prywatne. Są dostępne tylko dla klas znajdujących się wewnątrz tego samego pakietu klas, w którym znajduje się klasa ze zdefiniowanym obiektem. Każda klasa posiadająca tę samą deklarację pakietu (np. pakiet pl.helion.as3biblia {. . .}) będzie w stanie wywołać tę metodę lub uzyskać dostęp do zmiennej.
internal
Jest to domyślny modyfikator dostępu dla wszystkich obiektów w ActionScript 3.0. protected — ten modyfikator jest podobny do internal w tym,
że ogranicza dostęp dla pewnych klas. Do klasy protected można uzyskać dostęp tylko z klas, które rozszerzają funkcjonalność klasy (czyli z podklas). ActionScript 2.0 również posiada modyfikatory dostępu, ale są one ograniczone do public oraz private. Poza tym modyfikator dostępu private w ActionScript 2.0 odpowiada nowemu słowu kluczowemu protected. Słowo kluczowe private w ActionScript 3.0 bardziej ogranicza dostęp niż słowo kluczowe private w AS2, ponieważ właściwości i metody private nie są dziedziczone. Ponieważ w ActionScript 2.0 nie rozróżnia się typów danych podczas wykonywania aplikacji, słowo kluczowe private nie zawsze jest honorowane, a czasami do obiektów prywatnych można uzyskiwać dostęp spoza klasy. W ActionScript 3.0 spowoduje to wystąpienie błędu czasu wykonania. AS2 nie wymaga używania tych modyfikatorów dostępu. Chociaż nie są one również wymagane w AS3, kompilator wygeneruje ostrzeżenie, gdy zauważy brak modyfikatora.
ActionScript 3.0 wprowadza także koncepcję własnych atrybutów przestrzeni nazw, jednak omówimy to w rozdziale 3.
Zasięg Wszystkie zmienne i funkcje zdefiniowane w ActionScript 3.0 istnieją w pewnym zasięgu. Zasięg definiuje się jako obszar, w którym obiekt jest dostępny oraz w którym definiowana jest wartość. Aby zrozumieć to podejście, użyjemy analogii poziomów zarządzania. W większości krajów rząd zajmuje się sprawami dotyczącymi całego kraju, jak chociażby prawem podatkowym, a samorządy lokalne zajmują się lokalnymi zagadnieniami, np. finansowaniem jakiejś miejskiej szkoły. Rząd ogólnokrajowy nie jest w stanie zajmować
Rozdział 2. Podstawy języka ActionScript 3.0
57
się lokalnymi sprawami i na odwrót. W większości przypadków stosowane są prawa ogólnokrajowe, chyba że przesłaniają je prawa lokalne. Zasięg w programowaniu działa całkiem podobnie. Obiekty działają w pewnym zasięgu w zależności od tego, jak i gdzie są zdefiniowane. Ogólnie używane są obiekty zdefiniowane szerzej, chyba że są zastępowane lokalnymi wersjami. Przyjrzymy się różnym typom zasięgu, z którymi będziesz musiał pracować.
Typy zasięgu Zasięg może być globalny albo lokalny. Obiekty o zasięgu globalnym są dostępne z dowolnego miejsca w kodzie, podczas gdy obiekty o zasięgu lokalnym są dostępne tylko poprzez obiekt, w którym są zdefiniowane.
Zasięg globalny Wszystkie elementy definiowane na głównej osi czasu w filmie Flash lub na zewnątrz funkcji lub klasy mają zasięg globalny. Do tych obiektów można uzyskiwać dostęp z dowolnego innego zasięgu. Definiowanie obiektów na osi czasu jest zniechęcające i w większości przypadków będziesz pracować z zasięgiem lokalnym.
Zasięg lokalny Zasięg lokalny jest trochę bardziej skomplikowany. Występuje tu wiele warstw lokalizowanego dostępu (lub podzasięgów) zależnie od tego, jak obiekt jest zdefiniowany. Zmienne i metody na poziomie klasy (statyczne) Najszerzej definiowanymi obiektami lokalnymi są zmienne i metody statyczne (lub na poziomie klasy). Obiekty te występują w zasięgu klasowym. Innymi słowy, należą do samej klasy, a nie do instancji klasy, czyli konkretnego obiektu tej klasy. Obiekty te są definiowane poprzez użycie słowa kluczowego static: package { public class ScopeTest { public static var foo:String = "bar"; } }
Dostęp do nich uzyskuje się poprzez użycie nazwy klasy, po której występuje nazwa obiektu. Na przykład: trace(ScopeTest.foo); // wyświetli: bar
Kod zdefiniowany wewnątrz klasy może wykorzystywać takie zmienne lub metody bez konieczności używania nazwy klasy w celu dostępu do nich, ale wszystkie instancje tej klasy będą posiadać te same wartości statyczne. Ponieważ klasy są dostępne z dowolnego miejsca (o ile są zaimportowane i publiczne), zmienne na poziomie klasy są tak dobre jak globalne.
58
Część I Rozpoczęcie pracy z ActionScript 3.0
Zmienne i metody na poziomie instancji Klasy mogą też definiować zmienne i metody istniejące w każdej instancji klasy. Większość zmiennych oraz metod, z którymi będziesz pracować, będzie znajdować się właśnie w tym zasięgu: package { public class ScopeTest { public static var foo:String = "bar"; public var answer:Number = 42; } }
W przeciwieństwie do zmiennych statycznych zmienne instancji będą niezależne dla każdej instancji klasy. Aby uzyskiwać dostęp do zmiennych instancji, będziesz używać nazwy instancji, po której wystąpi kropka i nazwa zmiennej: var myTest:ScopeTest = new ScopeTest(); trace(myTest.answer) // wyświetli: 42
Wszystkie zmienne instancji są automatycznie zwalniane z pamięci, gdy dana instancja zostaje usunięta. Mogą jednak pozostać w pamięci, jeśli odwołują się do nich inne obiekty. Jeśli zarządzanie pamięcią jest dla Twojego programu istotną kwestią, to zanim przystąpisz do usuwania klas i ich członków, upewnij się, czy czasem nie pozostały jakieś aktywne odwołania do nich.
Zmienne i funkcje na poziomie funkcji Ostatecznie obiekty mogą też być definiowane wewnątrz funkcji. Każda funkcja tworzy tymczasowy zasięg, który istnieje w czasie jej wykonywania. Zmienne definiowane wewnątrz funkcji mogą być wykorzystywane do tymczasowego przechowywania wartości i są usuwane po zakończeniu funkcji. W poniższym przykładzie zmienna message jest zdefiniowana jedynie podczas wykonywania funkcji showGreeting(). package { public class ScopeTest { public static var foo:String = "bar"; public var answer:Number = 42; public function showGreeting():void { var message:String = "Witaj, świecie!"; trace(message); } } } var myTest:ScopeTest = new ScopeTest(); myTest.showGreeting(); // wyświetli: Witaj, świecie! trace(myTest.message); // undefined
Zmienne zdefiniowane wewnątrz funkcji nie będą dostępne, dopóki funkcja nie zostanie wykonana. Zmienne zdefiniowane w klasie mogą być używane z wnętrza funkcji, ale nie na odwrót.
Rozdział 2. Podstawy języka ActionScript 3.0
59
Parametry funkcji są zawsze traktowane tak, jakby były zdefiniowane wewnątrz zasięgu funkcji. Funkcje również mogą być definiowane wewnątrz innych funkcji. Czasami nazywa się to zagnieżdżaniem funkcji. Takie zagnieżdżone funkcje będą dostępne tylko jako wsparcie dla funkcji, w których są zagnieżdżone. Zmienne na poziomie bloku Niektóre języki programowania pozwalają na definiowanie zmiennych wewnątrz bloków, takich jak pętle, cechujących się jeszcze mniejszym zasięgiem od zasięgu funkcji. Zapamiętaj, że ActionScript nie obsługuje blokowego poziomu zasięgu — zasięg funkcji jest najmniejszym zasięgiem.
Łańcuch zasięgu Łańcuch zasięgu, pokazany na rysunku 2.1, jest wewnętrznym mechanizmem utworzonym do zarządzania zasięgiem zmiennych podczas wykonywania funkcji. Po wystąpieniu odwołania do zmiennej Flash Player szuka deklaracji zmiennych, rozpoczynając od ostatnio wywołanej funkcji. Jeśli wartość nie zostanie odnaleziona w najbardziej lokalnym zakresie, przechodzi o jeden poziom wyżej do funkcji, która wywołała tę funkcję, i tam sprawdza. Proces ten jest kontynuowany aż do momentu, w którym łańcuch zasięgu zostanie sprawdzony w całości, aż do zasięgu globalnego.
Przesłanianie zmiennych Zgodnie z tym, co napisane zostało wcześniej, obiekty zdefiniowane w najbardziej lokalnym zasięgu będą zawsze przesłaniać obiekty zdefiniowane w bardziej globalnych zasięgach. Zmienne instancji są również dostępne w zasięgu funkcji, jednak wszystko, co zdefiniowane zostanie za pomocą słowa kluczowego var, utworzy nową lokalną instancję w zasięgu, w którym jest definiowane. Oto przykład: package { public class Local { public var a:String = "instancja"; public var b:String = "instancja"; public function method():void { var a:String = "funkcja"; b = "funkcja"; trace(a); trace(b); } } }
Zauważ, że wewnątrz funkcji nazwanej method za pomocą słowa kluczowego var definiujesz łańcuch znaków o nazwie a, a także używasz łańcucha o nazwie b bez zastosowania var.
60
Część I Rozpoczęcie pracy z ActionScript 3.0
Rysunek 2.1. Przykład łańcucha zasięgu
var myLocal:Local trace(myLocal.a); trace(myLocal.b); myLocal.method(); trace(myLocal.a); trace(myLocal.b);
= new Local(); // wyświetli: instancja // wyświetli: instancja // wyświetli: funkcja // wyświetli: instancja // wyświetli: funkcja
Rozdział 2. Podstawy języka ActionScript 3.0
61
W przypadku a funkcja definiuje swoją własną zmienną, nazwaną a, będącą zmienną na poziomie funkcji, tymczasowo przesłaniającą zmienną a instancji. W przypadku b zmienna instancji jest użyta wewnątrz funkcji (ponieważ nie zostało użyte słowo var) i jest jej przypisywana nowa wartość "funkcja". Jeśli chcesz zachować większą przejrzystość co do tego, która zmienna jest używana, zalecamy Ci używanie słowa kluczowego this podczas odwoływania się do zmiennej instancji posiadającej tę samą nazwę, co zmienna funkcji: public function setFoo (foo:String):void { this.foo = foo; }
W tym przypadku wartość zmiennej instancji o nazwie foo zostanie nadpisana wartością przekazaną do metody.
Praca z zasięgiem Wiedza na temat zasięgów jest niezbędna, ale jak możesz wykorzystać te informacje? Normalnie najlepiej jest definiować zmienną o możliwie najmniejszym zasięgu. Zmienne lokalne są łatwiejsze do uprzątnięcia, dzięki czemu nie mamy do czynienia z ich nadmiernym występowaniem, jednak nie oznacza to wcale, że powinieneś zawsze dążyć do używania zmiennych na poziomie funkcji, zwłaszcza jeśli nie będą najlepszym rozwiązaniem. Najprostszym sposobem odpowiedzenia na postawione wyżej pytanie jest określenie zastosowania tworzonego obiektu. Zadaj sobie pytanie, kto musi wiedzieć o istnieniu tworzonego obiektu.
Czy zmienna należy do klasy, czy do instancji klasy? Zmienne statyczne są przydatne, ponieważ są dostępne prawie z każdego miejsca w kodzie. Używanie ich może być kuszące, jednak lepiej zastanowić się, czy zmienna przypisana jest do klasy, czy może do instancji klasy. Jeśli np. modelujesz klasę LiterSizedBottle z dwoma właściwościami, capacity oraz contents, to w jaki sposób zostanie użyta każda z tych zmiennych? Pojemność litrowej butelki będzie taka sama dla wszystkich litrowych butelek — jeden litr. Z tego też powodu capacity powinna być zmienną klasową. Z drugiej strony, każda butelka może mieć inną zawartość. Jedna może zawierać wodę, podczas gdy druga będzie zawierać silny przemysłowy środek czyszczący. Zmienna contents powinna być zatem zmienną instancji.
Czy będę potrzebować tego obiektu po zakończeniu funkcji? Czy muszę zapisywać zmiany tej zmiennej? Określając, czy użyty ma zostać zasięg na poziomie funkcji, czy może na poziomie instancji, możesz zadać sobie powyższe pytanie. Ogólnie rzecz biorąc, jeśli zauważysz, że używasz tej samej zmiennej w dwóch różnych funkcjach, możesz zdefiniować ją jako zmienną
62
Część I Rozpoczęcie pracy z ActionScript 3.0
instancji. Wszędzie tam, gdzie wartość musi zostać zachowana wewnątrz obiektu po zakończeniu wykonywania funkcji, powinieneś użyć zmiennej instancji.
Typy danych ActionScript 3.0 jest typowanym, obiektowym językiem programowania. Oznacza to, że każdemu obiektowi (zmiennej) jest przypisany typ danych odwołujący się do rodzaju danych, jakie mogą być w nim przechowywane. Typ danych pomaga kompilatorowi oraz programowi Flash Player w określeniu, jakiego typu wartości mogą się spodziewać. Może być używany do ustawiania typu zmiennej lub parametru funkcji albo typu zwracanego.
Deklarowanie typów Do zadeklarowania typu danych zmiennej używamy operatora dwukropka z prawej strony nazwy zmiennej. Wskazuje to kompilatorowi, że jedynie wartości zgodne z zadeklarowanym typem danych mogą być przechowywane w tej zmiennej. var var x = x = x = x = x =
x:Number; name:String = "Mims"; 42; // bez problemu -13; // bez problemu 3.141; // bez problemu "foo"; // Zgłosi błąd podczas kompilacji! name; // Zgłosi błąd podczas kompilacji!
Ponieważ zmienna x została zadeklarowana jako typ Number, wartość tekstowa "foo" nie może zostać w niej umieszczona. Kompilator sprawdza, czy wszystkie wchodzące typy danych są kompatybilne. Gdy rozpozna niezgodność, sygnalizuje błąd. Z jakiego powodu możesz chcieć sygnalizować błąd? Czasami błędy mogą być czymś naprawdę dobrym. Wyobraź sobie, co by się działo, gdyby nie było błędów. Wartość x nagle stałaby się łańcuchem znaków. Przy następnej próbie użycia jej jako liczby rezultat nie miałby sensu ("foo" * 8.5 = ???). Zachowywanie typów danych pomaga w zachowaniu bardziej przewidywalnego i stabilnego kodu. W większości przypadków typ danych jest taki sam jak klasa, której obiekt jest instancją. Typ danych może być jednak również klasą rodzica (superklasa), z której dziedziczy klasa obiektu; lub interfejsem, który implementuje. Np. klasa flash.display.Sprite dziedziczy z wielu innych klas i interfejsów. Zmienna, która przechowuje obiekt typu Sprite, może używać dowolnej z następujących klas lub interfejsów jako typu danych: Object, EventDispatcher, IEventDispatcher, DisplayObject, DisplayObjectContainer, InteractiveObject lub IBitmapDrawable. Poniższy kod jest zatem całkowicie poprawny: var mySprite:Sprite = new Sprite(); var myDisplayObject:DisplayObject = mySprite;
Typ danych zmiennej definiuje typ danych akceptowany do przechowywania w tej zmiennej. Podczas gdy wszystkie obiekty Sprite są obiektami DisplayObject, nie wszystkie obiekty DisplayObject są obiektami Sprite. Z tego też powodu następujący kod byłby błędny: var mySprite:Sprite = new DisplayObject(); // błąd typu!
Rozdział 2. Podstawy języka ActionScript 3.0
63
Używanie zmiennych bez typu z typem danych wieloznacznika (*) Od czasu do czasu możesz być zmuszony do zapisania wartości w zmiennej bez dokładnej wiedzy na temat typu danych, które będzie przechowywać. W tym celu AS3 pozwala zastosować typ danych wieloznacznika (*). Symbol * oznacza, że typ danych nie jest znany aż do czasu wykonania (dynamiczny) lub że będzie akceptowanych więcej niż jeden typ danych (np. zarówno łańcuchy znaków, jak i liczby). Ten wieloznacznik jest bardzo przydatny podczas tworzenia funkcji, które zachowują się w różny sposób zależnie od typu danych ich parametrów. AS3 używa symbolu * dla zmiennych bez typu. W AS2 zmienne bez typu definiowano poprzez pomijanie typu danych.
Powinieneś być ostrożny podczas używania tych typów, ponieważ niemożliwe będzie sprawdzanie typów dla tych zmiennych, a Twoje wyniki mogą być nieprzewidywalne.
Praca z operatorami Operatory są wbudowanymi poleceniami podobnymi do funkcji, operującymi na jednej lub wielu wartościach. Prawdopodobnie znasz już większość operatorów, ponieważ wiele z nich stosuje się również w równaniach matematycznych. Operatory — w przeciwieństwie do funkcji — nigdy nie zawierają nawiasów — nawet te będące wyrazami.
Operatory jedno- i dwuargumentowe Niektóre operatory, jak np. operator inkrementacji (++), są przypisywane do pojedynczego argumentu. Są to operatory jednoargumentowe. Inne do przeprowadzenia operacji wymagają dwóch argumentów, jak np. operator (+). Nazywa się je operatorami dwuargumentowymi. Występuje też pojedynczy operator trzyargumentowy, który pobiera trzy argumenty; omówiony zostanie w dalszej części tego rozdziału. Gdy stosuje się operatory, bardzo ważną kwestią jest dostarczenie odpowiedniej liczby argumentów. Nie jest to jednak szczególnie trudne, gdyż sposób ich użycia jest dość oczywisty (nie możesz dodać czegoś do niczego).
Kolejność operatorów Operatory zachowują pewien porządek operacji. Oznacza to, że operacje realizowane przez pewne operatory są wykonywane przed innymi działaniami. Dzięki temu funkcje matematyczne będą zachowywać się w sposób spójny z funkcjonowaniem kalkulatorów oraz prawami arytmetyki. Ogólnie rzecz biorąc, najpierw wykonywane są operacje bardziej złożone, a dopiero po nich operacje prostsze. ActionScript wykonuje operacje w następującej kolejności: 1. Wyrażenia zawarte w nawiasach są wykonywane, począwszy od najbardziej zagnieżdżonych w kierunku do zewnątrz.
64
Część I Rozpoczęcie pracy z ActionScript 3.0
2. Wyniki zwracane przez funkcje są obliczane z założeniem, że wszystkie ich argumenty posiadają jakąś wartość. 3. Operatory mnożenia, dzielenia oraz modulo (*, /, %) posiadają ten sam priorytet i są wykonywane od lewej do prawej. 4. Operatory dodawania i odejmowania (+, -) posiadają ten sam priorytet i są wykonywane od lewej do prawej. Nawiasów używa się do grupowania wyrażeń, dzięki czemu ich zawartość będzie obliczana wcześniej, przed przejściem do kolejnych operacji. Na przykład: trace(3 + 4 * 5); // wyświetli : 23 trace((3 + 4) * 5); // wyświetli : 35
Powszechnie używane operatory Poniżej przedstawiamy często stosowane operatory.
Arytmetyczne (+, -, *, /) Nie wymagają objaśniania poza tym, że mnożenie jest zapisywane za pomocą *, a nie x, natomiast do dzielenia wykorzystuje się symbol / zamiast ÷.
Modulo (%) Operator modulo zwraca resztę z dzielenia. Dzieli pierwszą liczbę przez drugą i zwraca tylko resztę. 0 % 1 % 2 % 3 % 4 % 5 % 6 % ...
3 3 3 3 3 3 3
// 0 // 1 // 2 // 0 // 1 // 2 // 0
Inkrementacja (++) i dekrementacja (--) Operatory te dodają lub odejmują 1 od liczby, dla której zostaną użyte. Są operatorami jednoargumentowymi: var x:Number = 5; x++; // 6 x--; // 5
Gdy symbol inkrementacji lub dekrementacji zostanie umieszczony przed argumentem, 1 zostaje dodane od razu, zanim przetworzone zostaną inne zmienne.
Rozdział 2. Podstawy języka ActionScript 3.0
65
Złożone operatory przypisania Złożone operatory przypisania są skrótowym rozwiązaniem dla przeprowadzania obliczeń arytmetycznych na liczbach oraz ponownego przypisywania wyniku do liczby. Np. += doda wartość z prawej strony do wartości po lewej stronie i przypisze ponownie wynik wartości znajdującej się z lewej strony. x += 1;
oznacza to samo, co zapis: x = x + 1;
Poniższy fragment kodu przedstawia więcej przykładów złożonych operatorów przypisania. var x:Number = 0; x += 5; // x = 5 x -= 3; // x = 2 x *= 3; // x = 6 x /= 2; // x = 3 x %= 3; // x = 0
Listę wszystkich operatorów oraz sposób ich stosowania znajdziesz w dokumentacji AS3 (http://livedocs.adobe.com/flex/3/langref/operators.html).
Dokonywanie logicznych wyborów za pomocą instrukcji warunkowych Jaki sens miałoby wykonywanie Twoich programów w ten sam sposób podczas każdego uruchomienia? Zatracony zostałby aspekt wyboru sposobu postępowania w różnych zaistniałych okolicznościach. W tym miejscu do akcji wkraczają instrukcje warunkowe lub punkty podejmowania decyzji. Instrukcje warunkowe, takie jak wyrażenie if, umożliwiają określenie poprawności wyrażenia logicznego, co z kolei daje programowi możliwość stosowania różnych rozwiązań dla różnych rezultatów. Wszystkie instrukcje warunkowe operują na wartościach boolowskich. Wartości boolowskie, nazwane po matematyku George’u Boole’u, są prostymi logicznymi obiektami przyjmującymi tylko jedną z dwóch wartości: true (prawda) albo false (fałsz). Wszelkie logiczne wyrażenia, jak np. operatory użyte w poniższych przykładach, powinny zwracać boolowskie rezultaty, co pozwoli na wykorzystanie ich w instrukcjach warunkowych.
Instrukcja if Najpowszechniej używanym typem instrukcji warunkowej jest instrukcja if. Pozwala ona na wykonanie fragmentu kodu tylko w przypadku spełnienia pewnego warunku: jeśli ten warunek jest prawdziwy, wykonaj to. if (wyrażenie logiczne) { // kod, który ma być wykonany, gdy warunek będzie spełniony }
66
Część I Rozpoczęcie pracy z ActionScript 3.0
To wyrażenie sprawdza, czy wartość zmiennej weather jest równa łańcuchowi znaków "deszcz". Jeśli tak jest, wywołuje funkcję bringUmbrella. Możesz przeczytać to jako „Jeżeli pada deszcz, to zabierz parasolkę”. if (weather == "deszcz") { bringUmbrella(); }
{ } są opcjonalne dla instrukcji jednowierszowych, jednak zalecamy stosowanie nawiasów klamrowych w celu zachowania jednolitości.
Równość (==) We wcześniejszym przykładzie po słowie kluczowym if występuje w nawiasach wyrażenie logiczne, którego wartość ma zostać wyznaczona. Zwróć uwagę, że występują tam dwa znaki równości, a nie jeden. Ten podwójny znak równości znany jest jako operator równości (==). Stosowany jest do sprawdzania, czy wartość z lewej strony wyrażenia jest równa wartości z prawej strony. Różni się od operatora wykorzystującego pojedynczy znak równości (=), przypisującego zmiennej z lewej strony wartość znajdującą się po prawej stronie. Podwójny znak równości jest operatorem porównywania logicznego, co oznacza, że daje w rezultacie pojedynczy wynik — prawdę lub fałsz. W tym przypadku sprawdzamy, czy wartość zmiennej weather jest równa łańcuchowi "deszcz". Użycie pojedynczego znaku równości zamiast podwójnego znaku równości w wyrażeniu porównującym może dawać dziwne i nieprzewidywalne wyniki. Jest to błąd bardzo często popełniany przez programistów wszystkich języków. Na szczęście wprowadzone zostały ulepszenia kompilatora pomagające w automatycznej identyfikacji tego błędu. Niezależnie od tych udoskonaleń bądź ostrożny i sprawdzaj gruntownie swoje wyrażenia porównujące.
Po wyrażeniu logicznym mamy blok kodu wykonywany, jeśli wyrażenie zostanie uznane za prawdziwe. W tym przypadku wywołujemy funkcję nazwaną bringUmbrella(). Jeśli pogoda nie będzie deszczowa, ten blok kodu nie zostanie wykonany.
Testowanie innych porównań Słowo kluczowe if może zostać użyte nie tylko do wyznaczenia prostej równości. ActionScript zawiera wiele innych typów operatorów porównujących oraz operatorów logicznych, nadających się do stosowania samodzielnego lub łączenia w bardziej złożone typy porównań.
Większy od (>) oraz mniejszy od (. Poniższy kod mówi: „Jeśli opady są większe od 0, to pogoda jest deszczowa”. if (rainfall > 0) { weather = "deszcz" }
Możesz też używać operatorów mniejszy lub równy (=):
Rozdział 2. Podstawy języka ActionScript 3.0
67
var x:int = 0; var a:int = -1; var b:int = 0; var c:int = 1; trace(x trace(x trace(x trace(x trace(x trace(x
= = =
a); a); b); b); c); c);
// wyświetli: false // wyświetli: true // wyświetli: true // wyświetli: true // wyświetli: true // wyświetli: false
Nierówność (!=) Dodanie znaku wykrzyknika przed wyrażeniem logicznym spowoduje wyznaczenie wyrażenia przeciwnego. Operator ten często nazywany jest operatorem negacji (!). ActionScript zawiera operator nierówności (!=), który — jak się zapewne spodziewasz — sprawdza, czy wartość z lewej strony wyrażenia jest różna od wartości z prawej strony: if (weather != "słońce") { bringUmbrella(); }
Wyrażenie to wywołuje funkcję bringUmbrella(), jeśli na dworze nie jest słonecznie. Innym sposobem zapisu tego fragmentu kodu byłoby użycie operatora negacji do zanegowania równości: if (!(weather == "słońce")) { bringUmbrella(); }
Oba wiersze kodu dają identyczne rezultaty.
Operatory oraz (&&) i lub (||) Jeśli chcesz połączyć ze sobą wiele warunków, możesz spróbować zagnieździć lub powielić swoje wyrażenia if zgodnie z poniższym przykładem: if if if if
(weather == "deszcz") { bringUmbrella(); } (weather == "deszcz ze śniegiem") { bringUmbrella(); } (weather == "śnieg") { bringUmbrella(); } (weather != "deszcz") { if (weather != "deszcz ze śniegiem") { if (weather != "śnieg") { leaveUmbrella(); } }
}
Jak widzisz, napisanie takiego kodu wymaga sporego nakładu pracy. Na szczęście ActionScript umożliwia stosowanie dwóch logicznych operatorów pozwalających łączyć wiele warunków w jedno wyrażenie if. Są to operatory: oraz (&&) i lub (||). Gdy używa się operatora oraz, wszystkie warunki muszą być prawdziwe, aby kod mógł zostać wykonany. Możesz powiedzieć „Jeśli to oraz to jest prawdą, zrób to”. Gdy używa się operatora lub, każdy prawdziwy warunek powoduje wykonanie kodu. Znaczenie tego operatora jest mniej więcej takie: „Jeśli to lub tamto jest prawdą, zrób to”. Przepiszmy jeszcze raz nasz przykład, tym razem z wykorzystaniem operatorów oraz i lub.
68
Część I Rozpoczęcie pracy z ActionScript 3.0 if (weather == "deszcz" || weather == "deszcz ze śniegiem" || weather == "śnieg") { bringUmbrella(); } if (weather != "deszcz" && weather != "deszcz ze śniegiem" && weather != "śnieg") { leaveUmbrella(); }
Sprawdzanie wystąpień wartości null Jeśli pracujesz z obiektami, które w AS3 mogą przyjmować wartość null, to przed pozyskaniem dostępu do metod lub właściwości tych obiektów ważne jest sprawdzenie, czy przypadkiem wartość nie jest równa null (brak takiego sprawdzenia może później skutkować błędem czasu wykonywania). Sprawdzanie, czy wartość jest równa null, jest bardzo proste: if (weather != null) { checkWeather(); }
Jeśli pogoda nie ma wartości null, wywoływana jest metoda checkWeather(). Ten sam rezultat możesz też osiągnąć w następujący sposób: if (weather) { checkWeather(); }
Wartość zmiennej weather zostanie przekonwertowana na jej boolowski odpowiednik przed jej wyznaczeniem. W przypadku obiektów wszystko, co zostało zdefiniowane, zwraca wartość true, podczas gdy wartości null oraz undefined zwracają wartość false. Taki sposób sprawdzania jest prostszy w zapisie, ale jest mniej pewny, ponieważ daje różne rezultaty w zależności od typu danych sprawdzanego obiektu.
Używanie innych wartości boolowskich Funkcje zwracające wartości boolowskie mogą być wykorzystywane także w wyrażeniach warunkowych. Niektóre z takich funkcji definiowane są przez AS3, ale możesz również tworzyć swoje własne. Zgodnie z konwencją funkcje te zazwyczaj mają nazwy podobne do nazwy sprawdzanej przez nie logiki. Nazwy te rozpoczynają się od słowa "is" lub "has", jak np. nazwa funkcji isNaN() (is not a number — nie jest liczbą), która zwraca wartość true, jeśli przekazany argument nie jest reprezentacją Number, int, uint lub tekstową reprezentacją liczby takiej jak "5". if (isNaN(temperatura)) { updateTemp(); }
if..else Poprzez dodanie słowa kluczowego else możesz dołączyć dodatkowy blok kodu wykonywany tylko w przypadku niespełnienia warunku. Poniższy przykład może zostać odczytany w następujący sposób: „Jeśli pada deszcz, to zabierz parasolkę, w przeciwnym razie zostaw parasolkę”. if (weather == "deszcz") { bringUmbrella(); } else { leaveUmbrella(); }
Rozdział 2. Podstawy języka ActionScript 3.0
69
Możesz też dodać inną instrukcję if, która sprawdzona zostanie, tylko jeżeli wynik pierwszej instrukcji if nie jest prawdą. Poniższe wyrażenie mówi: „Jeśli pada deszcz, to zabierz parasolkę, ale jeśli pada śnieg, to zabierz płaszcz. Jeśli nie pada deszcz ani śnieg i jest słonecznie, zabierz olejek do opalania”. if (weather == "deszcz") { bringUmbrella(); } else if (weather == "śnieg"){ bringCoat(); } else if (weather == "słońce") { bringSunscreen(); }
switch Jeśli musisz sprawdzić jakiś warunek dla wielu możliwych opcji, jak w naszym ostatnim przykładzie, gdzie testowaliśmy przypadki deszczu, śniegu oraz słońca, najlepszym rozwiązaniem będzie użycie instrukcji switch. Instrukcja switch jest podobna do gigantycznej instrukcji if..else testującej pojedyncze wyrażenie względem wielu możliwości. Możesz sprawdzać warunki dla dowolnej liczby różnych przypadków lub przewidywanych wartości wyrażenia, a nawet określić domyślne działania na okoliczność, gdyby żaden z przypadków nie był odpowiedni. Zwróć uwagę na to, że w każdym bloku case stosowana jest instrukcja break. Instrukcja break każe instrukcji warunkowej lub pętli anulować resztę operacji bez przechodzenia dalej. Podczas pracy z instrukcją switch — przeciwnie niż podczas pracy z instrukcją if, wykonującą kod zawarty w nawiasach klamrowych ({ }) —musisz dołączać break po każdej instrukcji case w celu zapobiegnięcia kontynuacji wykonywania kodu z następnego przypadku. switch (weather) { case "deszcz" : bringUmbrella(); break; case "śnieg" : bringCoat(); break; case "słońce" : bringSunscreen(); break; default : bringBook(); }
Odpowiada to zapisowi: if (weather == "deszcz") { bringUmbrella(); } else if (weather == "śnieg") { bringCoat(); } else if (weather == "słońce") {
70
Część I Rozpoczęcie pracy z ActionScript 3.0 bringSunscreen(); } else { bringBook(); }
Chociaż nie jest to wymagane, zalecamy używanie słowa kluczowego default do definiowania przypadku obsługiwanego, gdy żaden z pozostałych warunków w instrukcji switch nie zostanie spełniony.
Operator warunkowy Ostatnim sposobem sprawdzenia poprawności wyrażenia jest operator warunkowy (?:). Operator ten wygląda trochę inaczej od pozostałych operatorów, ponieważ posiada dwie oddzielne części i wymaga trzech parametrów (jest to jedyny trzyargumentowy operator w języku ActionScript). Operator warunkowy zachowuje się podobnie jak podstawowa instrukcja if. (wyrażenie logiczne) ? jeśli prawda : jeśli fałsz;
Operator warunkowy przyjmuje jako parametry trzy wyrażenia. Pierwsze jest wyrażeniem logicznym, dla którego należy wyznaczyć wartość logiczną, podobnie jak dla wyrażenia przekazywanego do instrukcji if. Może to być porównanie, zmienna, wywołanie funkcji lub cokolwiek, dla czego można określić wartość prawda lub fałsz. Po wyrażeniu logicznym występuje pytajnik będący pierwszą częścią operatora. Bezpośrednio za pytajnikiem znajduje się wyrażenie, które zostanie przetworzone i zwrócone, jeżeli wyrażenie logiczne zostanie uznane za prawdziwe. Po nim następuje dwukropek, druga część operatora oraz ostatecznie wyrażenie, które zostanie zwrócone, jeśli pierwsza wartość zostanie uznana za fałszywą. Poniższy przykład przedstawia praktyczne zastosowanie operatora warunkowego. Tym razem sprawdzamy, czy pada grad: (weather == "grad") ? bringMotorcycleHelmet() : bringStrawHat();
Powyższy wiersz odpowiada zapisowi: if (weather == "grad") { bringMotorcycleHelmet(); } else { bringStrawHat(); }
Operatora warunkowego możesz też użyć do zwracania wartości, które mogą zostać zapisane w zmiennej. Następujący kod: hatType = (weather == "grad") ? "kask" : "słomkowy";
przypisze zmiennej hatType wartość kask lub słomkowy, w zależności od wartości zmiennej weather. Często spotykaną implementacją tego operatora jest ustawianie domyślnej wartości dla niezdefiniowanej zmiennej:
Rozdział 2. Podstawy języka ActionScript 3.0
71
weather = weather ? weather : "częściowe zachmurzenie";
Powyższy kod może wyglądać trochę dziwnie, ale jego działanie jest proste. Sprawdza, czy zdefiniowana jest zmienna weather, a jeśli nie, przypisuje jej wartość "częściowe zachmurzenie". Ponieważ większość obiektów w ActionScript daje w rezultacie true, jeśli posiadają wartość, oraz false, gdy mają wartość null lub są niezdefiniowane, kod weather ? da w rezultacie true, jeśli występuje definicja zmiennej; jeśli nie, wynikiem będzie false. Jedno z tych dwóch wyrażeń zostanie zwrócone i przypisane do zmiennej, dając w rezultacie: weather = weather
co albo nie spowoduje żadnych zmian, albo spowoduje przypisanie domyślnej wartości "częściowe zachmurzenie": weather = "częściowe zachmurzenie"
Chociaż takie instrukcje warunkowe są o wiele krótsze w zapisie, są też trudniejsze do odczytu i szybkiego zrozumienia. Wybierając pomiędzy nimi, musisz wszystko dobrze przemyśleć.
Powtarzanie operacji za pomocą pętli Pętle są kolejną strukturą sterującą używaną do zliczania w górę, w dół lub w zbiorze danych. Pozwalają na wielokrotne powtarzanie operacji różnego typu przy jednokrotnym zapisie kodu. Podobnie jak instrukcje warunkowe czynią programy bardziej interesującymi, wprowadzając zróżnicowanie działania, tak pętle czynią programy interesującymi poprzez umożliwianie przeprowadzania operacji, których nie bylibyśmy w stanie przeprowadzić ręcznie. A czy wykonywanie takich zadań nie jest czasem zadaniem dla komputera? Zastosowaniom pętli nie ma końca. Możesz chcieć np. przeczytać listę 100-elementową, zastosować obliczenie dla każdej grafiki widocznej na ekranie, szukać w tablicy jakiejś konkretnej wartości, zainicjalizować każdy obiekt listy czy po prostu wypisać wszystkie litery od A do Z. Najpowszechniejszym zastosowaniem pętli, z jakim się spotkasz, będzie przechodzenie przez kolejne elementy tablicy. Tablica jest uporządkowaną listą danych. Tablice mają właściwość length, która zwraca liczbę elementów w tablicy.
Użycie pętli for W ActionScript dostępnych jest kilka typów pętli. Rozpoczniemy od uwielbianej przez wszystkich pętli for. Jeśli używałeś wcześniej języka C, Java lub dowolnego innego języka programowania, prawdopodobnie znasz tę instrukcję. Zwyczajna pętla for wygląda mniej więcej tak: for (var i:int = 1; i = 0 && newHeight < Number.POSITIVE_INFINITY) { _height = newHeight; updateFloors(); } else { throw new RangeError("Wysokość musi być skończona i dodatnia"); } } public function getHeight():Number { return _height; } private function updateFloors():void { // Upewnij się, czy liczba pięter // odpowiada wysokości budynku } } }
W rzeczywistości wewnętrzna (internal) właściwość jest zamieniana na prywatną (private). Jedna z konwencji obsługiwania właściwości prywatnych za pomocą publicznych metod dostępowych określa, aby dodawać znak podkreślenia na początku nazwy właściwości prywatnej. Dwie zwykłe metody zostają dodane w celu ustawiania i pobierania właściwości. Na przykład dla właściwości o nazwie foo metody te powinny się nazywać setFoo() oraz getFoo(). Metody te nazywane są odpowiednio metodami ustawiającymi (albo w skrócie setterami, od angielskiego słowa setter) oraz metodami pobierającymi (w skrócie getterami – ang. getter).
116
Część I Rozpoczęcie pracy z ActionScript 3.0
W tym przykładzie metoda ustawiająca sprawdza, czy próba przypisania jest poprawna. Jeśli wynik weryfikacji jest prawdą, wykonuje się przypisanie, w przeciwnym razie klasa zgłasza błąd czasu wykonywania. Więcej informacji na temat obsługi błędów znajdziesz w rozdziale 19. Metoda ustawiająca wykorzystuje także możliwość uaktualnienia pięter budynku w oparciu o jego nową wysokość. W przeciwnym razie musiałbyś ciągle sprawdzać, czy obie te wartości są ze sobą zsynchronizowane. Zajmując się tym w jednym miejscu, być może będziesz w stanie wyeliminować ten problem. Inną rzeczą, jaką umożliwiają Ci metody dostępowe, jest praca z właściwościami pochodnymi: wartościami, które nie są przechowywane, ale obliczane na żądanie, udającymi przy tym zwykłe właściwości. Możesz np. dodać do klasy Building metodę zwracającą wysokość budynku w centymetrach: public function getHeightCentimeters():Number { return getHeight() * 100; }
Używając jawnych metod dostępowych, musisz używać wywołań metod w celu uzyskania dostępu do właściwości klasy. var b:Building = new pl.helion.as3biblia.explicit.Building(); b.setHeight(100); trace("Budynek ma ", b.getHeight(), " metrów wysokości");
Drugi sposób używania metod dostępowych umożliwia Ci stosowanie notacji kropkowej, tak jakby właściwości były publicznymi. Również tutaj przechwytywane jest przypisanie, aby możliwe było wykonanie tego, czego będziesz potrzebować. Niejawne metody dostępowe są specjalną funkcją języka i używają słów kluczowych get oraz set przed nazwami właściwości do zdefiniowania funkcji tych metod. package pl.helion.as3biblia.implicit { public class Building { // wysokość budynku w metrach private var _height:Number = 0; public function set height(newHeight:Number):void { if (!isNaN(newHeight) && newHeight >= 0 && newHeight < Number.POSITIVE_INFINITY) { _height = newHeight; updateFloors(); } else { throw new RangeError("Wysokość musi być skończona i dodatnia"); } } public function get height():Number { return _height; } private function updateFloors():void {
Rozdział 3. Programowanie z wykorzystaniem klas
117
// Upewnij się, czy liczba pięter // odpowiada wysokości budynku } public function get heightCentimeters():Number { return height * 100; } } }
Teraz, aby uzyskać dostęp do właściwości, możesz użyć notacji kropkowej, a przy tym w dalszym ciągu czerpać korzyści z kodu sprawdzającego poprawność przypisywanej wartości: var b:Building = new pl.helion.as3biblia.implicit.Building(); b.height = 1000; b.height = -12; // Błąd: Wysokość musi być skończona i dodatnia
Niejawnych metod dostępowych możesz też użyć do uczynienia właściwości właściwością tylko do odczytu. Dołącz publiczną niejawną metodę pobierającą, ale pomiń metodę ustawiającą. Kompilator przechwyci wszelkie próby ustawienia właściwości z zewnątrz klasy. W naszej przykładowej klasie Building właściwość heightInCentimeters oprócz tego, że jest właściwością pochodną, jest również tylko do odczytu. Niejawne metody pobierające i ustawiające są z natury publiczne. Definiując je, zawsze musisz używać słowa kluczowego public.
Unikanie efektów ubocznych Właśnie pokazaliśmy, jak uruchamiać dowolny kod, gdy wykonane jest przypisanie do właściwości, ale ważne w tym momencie jest zwrócenie uwagi na pewne ostrzeżenie. Stosowanie metod dostępowych do kontrolowania dostępu do właściwości i zapewniania, że zmodyfikowana właściwość ma bezpośredni wpływ na wszystko, co od niej jest zależne, jest dobrym pomysłem. Jednak gdy wywołujesz metodę obiektu w celu wykonania jakiegoś zadania, a oprócz tego obiekt wykonuje coś niezwiązanego z tym zadaniem, to pojawia się efekt uboczny. I chociaż czasami może to być całkiem wygodne, ogólnie rzecz biorąc powinno się tego unikać. Powinieneś projektować swoje klasy, metody i właściwości tak, aby wykonywały to, na co wskazują ich nazwy, i nic więcej. Metody klasy mogą wywoływać efekty uboczne podobnie jak metody dostępowe. Dlatego pamiętaj o tym, że ustawiane wartości oraz wykonywane operacje powinny pozostać oddzielone. Używanie niejawnych metod dostępowych do usprawniania obsługi wartości może być pożyteczne, ale może powodować skutki uboczne, których nie wszyscy używający kodu mogą być świadomi.
Przesłanianie zachowania Stosując dziedziczenie, możesz czasami chcieć, aby klasa potomka wykonała coś, co robi również klasa rodzica, ale w inny sposób. Możesz chcieć utworzyć np. klasę QuietSeagull wydającą o wiele delikatniejszy odgłos szczebiotania. Superklasa lub klasa bazowa Seagull
118
Część I Rozpoczęcie pracy z ActionScript 3.0
posiada już zachowanie squawk(), a ponieważ rozszerzasz tę klasę, Twoja nowa klasa to zachowanie dziedziczy. Proste dziedziczenie jest dla klasy zaledwie punktem wyjściowym, na który składają się wszystkie publiczne, wewnętrzne i chronione właściwości oraz metody klasy bazowej. Jednak czasami zwykłe dodanie czegoś do tego punktu początkowego nie wystarcza. Czasem trzeba go zmienić. Możesz przesłonić metody klasy bazowej poprzez użycie słowa kluczowego override. W tym celu umieszczasz to słowo przed lub po modyfikatorze dostępu. Nie jest natomiast możliwe przesłanianie prywatnych metod superklasy, ponieważ nie masz do nich dostępu. Klasa QuietSeagull może wyglądać mniej więcej tak: package { public class QuietSeagull extends Seagull { override public function squawk():void { // Bardzo uprzejma mewa nie szczebioce // niezależnie od tego, co robią jej rodzice. trace("..."); } } }
Gdy teraz utworzysz różne rodzaje mew i każesz im zaszczebiotać, przekonasz się, że oryginalna klasa Seagull oraz wszystkie klasy rozszerzające ją bez przesłaniania metody squawk() zachowują się tak samo. Tylko QuietSeagull maszeruje do rytmu swojego bardzo cichego werbla: var normalGull:Seagull = new Seagull(); var sportyGull:Seagull = new SoccerSeagull(); var quietGull:Seagull = new QuietSeagull(); normalGull.squawk(); // Mewa mówi 'SKWAAA!' sportyGull.squawk(); // Mewa mówi 'SKWAAA!' quietGull.squawk(); // ...
Przesłaniać możesz tylko metody klasy. Nie możesz przesłaniać właściwości, jak chociażby wagi mewy. Można to jednak obejść, wykorzystując w tym celu metody dostępowe. Ponieważ metody te są tylko metodami, mogą być (dotyczy to nawet niejawnych metod dostępowych) przesłaniane, tak aby w podklasie zwracały inną wartość. Możesz np. przekonwertować klasę bazową Seagull, udostępniając jej właściwość weight za pomocą metod dostępowych zamiast jako właściwość publiczną, a następnie przesłonić te metody dostępowe. Uboczną korzyścią płynącą z tego jest fakt, że waga mewy może się stać wtedy wartością tylko do odczytu i będziesz mógł kontrolować wagę wewnętrznie jako reakcję na jedzenie i inne zachowania, ale zabroniona będzie modyfikacja wagi z zewnątrz. W pliku Seagull.as: package { public class Seagull {
Rozdział 3. Programowanie z wykorzystaniem klas
119
public function get weight():Number { return 2; }
}
}
public function squawk():void { trace("Mewa mówi 'SKWAAA!'"); }
W pliku HungrySeagull.as: package { public class HungrySeagull extends Seagull { override public function get weight():Number { return 1.5; } } }
Przesłanianie metod (ang. overriding) jest czym innym niż przeciążanie metod (ang. overloading). Przeciążanie zezwala, aby dwie metody miały taką samą nazwę, jeśli tylko mają różne listy argumentów. W ActionScript 3.0 nie jest to jednak możliwe. Przesłaniające metody muszą mieć dokładnie tę samą sygnaturę, co oryginalna metoda. W ActionScript 3.0 możesz tylko przesłaniać metody.
Używanie klasy bazowej Gdy przesłaniasz metodę, nie musisz wyrzucać wszystkiego, co robiła dla Ciebie klasa bazowa. W dalszym ciągu masz dostęp do nieprywatnych członków i właściwości superklasy poprzez obiekt super. Używaj obiektu super, aby rozszerzać istniejącą metodę lub wywoływać funkcjonalność superklasy, która została przesłonięta. Możesz utworzyć bardzo uprzejmą mewę robiącą wszystko to, co robi zwyczajna mewa, ale po wszystkim przepraszającą za swoje zachowanie. Aby mogło to zadziałać, musisz zmodyfikować główne metody mewy, ale jednocześnie wykorzystać ich bieżący sposób funkcjonowania. Zamiast kopiować kod, uzależniasz jego działanie od implementacji superklasy. Oznacza to także, że zmiany w implementacji metod klasy bazowej będą miały bezpośredni wpływ na jej podklasy: package { public class PoliteSeagull extends Seagull { override public function squawk():void { super.squawk(); trace("Wstydliwa mewa zakrywa twarz ze wstydu."); } override public function fly():void
120
Część I Rozpoczęcie pracy z ActionScript 3.0 { super.fly(); trace("Mewa ląduje i przeprasza za zasłanianie słońca."); } override public function eat():void { trace("Mewa przeprasza zwierzę, które zamierza zjeść."); super.eat(); } }
}
Powyższy przykład uprzejmej mewy pokazuje, że możliwe jest wywołanie metod klasy bazowej niezależnie od tego, czy wywołasz je na początku, w środku, czy na końcu dodawanego kodu. var politeGull:Seagull = new PoliteGull(); // Pojawia się nowa mewa politeGull.eat(); /* Mewa przeprasza zwierzę, które zamierza zjeść. Mewa wbija dziób w piasek, wyciąga maleńkiego kraba i zjada go jednym połknięciem */
W tym przykładzie konstruktor nie został określony, dlatego użyty został konstruktor domyślny. Z działania programu widać, że wywołuje on konstruktor superklasy, ponieważ na wyjściu widoczny jest napis: „Pojawia się nowa mewa”. Mógłbyś wykonać coś w stylu przesłonięcia konstruktora, ale w rzeczywistości każda klasa posiada swój unikalny konstruktor. Zamiast stosować słowo kluczowe override, napisz swój własny konstruktor dla tej nowej klasy. Ponieważ jest to nowa unikalna metoda, konstruktory superklasy oraz podklasy mogą posiadać zupełnie różne argumenty, w przeciwieństwie do przesłanianych metod. W powyższym przykładzie widzisz wywołanie konstruktora superklasy, ponieważ domyślny konstruktor składa się tylko z jednego wiersza: specjalnej funkcji o nazwie super(). Metoda ta jest związana z konstruktorem superklasy. Możesz używać tej metody do kontrolowania sposobu wywoływania konstruktora superklasy, ale nie możesz zapobiec wywoływaniu konstruktora superklasy. Jeśli chcesz wywołać metodę super(), musisz to zrobić w pierwszym wierszu konstruktora.
Używanie statycznych metod i właściwości Metody instancji oraz właściwości instancji są elementami zdefiniowanymi w klasie obiektu i istnieją w obiekcie. Klasa Bicycle może posiadać właściwość odpowiadającą liczbie biegów w rowerze. package { public class Bicycle { private var _gears:Number; public function get gears():Number { return _gears; }
Rozdział 3. Programowanie z wykorzystaniem klas
121
public function Bicycle(numberOfGears:Number) { this._gears = numberOfGears; } }
}
Ten przykład wykorzystuje metody dostępowe z dobrym skutkiem, umożliwiając Ci utworzenie nowego obiektu Bicycle z dowolną liczbą biegów, ale nie pozwalając na zmianę liczby biegów już po jego utworzeniu. Czyni to poprzez przypisanie liczby biegów w konstruktorze i brak metody ustawiającej dla tej właściwości. Kryjąca się za tym koncepcja polega na tym, że zmienna instancji, taka jak gears, należy do konkretnej instancji. Każda instancja klasy Bicycle posiada własną liczbę biegów. var eighteenSpeed:Bicycle = new Bicycle(18); var fixedGear:Bicycle = new Bicycle(1); trace(eighteenSpeed.gears); // 18 trace(fixedGear.gears); // 1
Jest jeszcze wiele właściwości roweru, które mógłbyś chcieć zamienić na zmienne instancji, jak kolor roweru, jego rozmiar czy typ opon w rowerze. Występuje też kilka właściwości, które są częścią definicji roweru. Rozważając zbiór rowerów tradycyjnych, możemy uznać, że wszystkie z definicji posiadają dwa koła. Te właściwości mogą być modelowane poprzez zmienne statyczne.
Zmienne statyczne ActionScript 3.0 umożliwia definiowanie metod oraz właściwości należących do samej klasy, a nie do jej instancji. W związku z tym, że liczba kół jest częścią definicji roweru, a nie właściwością samego roweru, możesz dodać tę właściwość do klasy zamiast do instancji. Takie właściwości nazywa się zmiennymi klasy lub zmiennymi statycznymi. package { public class Bicycle { public static var wheels:Number = 2; private var _gears:Number; public function get gears():Number { return _gears; } public function Bicycle(numberOfGears:Number) { this._gears = numberOfGears; } }
}
122
Część I Rozpoczęcie pracy z ActionScript 3.0 Słowo kluczowe static może być pisane przed lub po modyfikatorze dostępu. Konwencja mówi, aby pisać je po modyfikatorze, jak w powyższym przykładzie.
Poprzez dodanie słowa kluczowego static definiujesz zmienną wheels jako część klasy, a nie jako część instancji. Wszystkie obiekty Bicycle będą teraz posiadać wspólną zmienną wheels. Nawet jeśli utworzysz flotę tysiąca rowerów, to wszystkie będą używać tej wspólnej zmiennej klasowej i każda jej zmiana będzie miała bezpośredni wpływ na wszystkie te obiekty. Teraz, gdy wheels jest zmienną statyczną, używać jej będziesz w inny sposób. Należy do klasy Bicycle, dlatego używać będziesz notacji kropkowej, aby uzyskać do niej dostęp z klasy (nie z instancji). var fixedGear:Bicycle = new Bicycle(1); trace(fixedGear.wheels); // Źle! Błąd kompilatora trace(Bicycle.wheels); // Poprawnie! 2
Stosuje się również pozostałe reguły dostępowe, dlatego wystarczy napisać Bicycle. wheels, ponieważ zmienna wheels została zdefiniowana jako publiczna. Pamiętaj o tym, że klasę Bicycle widzisz tylko dlatego, że ona również jest publiczna. Kod wewnątrz klasy może uzyskiwać dostęp do zmiennej wheels bez pisania Bicycle. wheels. Ponieważ znajduje się w tej samej klasie, w której zdefiniowana jest zmienna statyczna, zmienna ta znajduje się w jego zasięgu. Wiele osób twierdzi jednak, że odwoływanie się do zmiennych statycznych za pomocą nazwy ich klasy jest elementem dobrego stylu programowania. Poniżej do konstruktora Twojej klasy Bicycle dodajesz kod wykorzystujący zmienną statyczną. package { public class Bicycle { static public var wheels:Number = 2; private var _gears:Number; public function get gears():Number { return _gears; }
}
}
public function Bicycle(numberOfGears:Number) { this._gears = numberOfGears; for (var i:int = 0; i < Bicycle.wheels; i++) { // Przygotuj koło. } }
Rozdział 3. Programowanie z wykorzystaniem klas
123
Stałe statyczne Skoro liczba kół jest częścią definicji roweru, to zmienna wheels nie tylko powinna należeć do klasy Bicycle, ale powinna być również stałą. Możesz bez problemów używać stałych statycznych, czyli stałych należących do klasy, a nie do instancji. package { public class Bicycle { static public const WHEELS:Number = 2; // reszta klasy } }
Okazuje się, że większość wartości, które powinny pozostawać stałe, musi też pozostać stałymi we wszystkich instancjach klasy. Nie jest to regułą, ale często spotykaną sytuacją. Na przykład prędkość graniczna, punkt topnienia ołowiu czy liczba liter w alfabecie są stałe, ale również są stałymi zachowującymi swoją wartość dla wszystkich instancji i nadającymi się do zapisu w postaci statycznej. Jeśli stała nie zmienia wartości dla różnych instancji klasy i musisz mieć możliwość uzyskania dostępu do stałej bez tworzenia instancji klasy ją posiadającej, używaj stałych statycznych. Stałe statyczne umożliwiają również stosowanie niektórych bardzo pożytecznych technik, o których warto tutaj wspomnieć. Porównania łańcuchów oraz parametry łańcuchowe mogą być bardzo kłopotliwe. Przyjrzyj się poniższej funkcji, którą możemy umieścić w klasie Bicycle: package { public class Bicycle { public function performTrick(trickName:String):void { switch (trickName) { case "wheelie": // kod przechodzi tutaj do jazdy na tylnym kole break; case "bunnyhop": // kod przechodzi tutaj do podskoku break; case "stoppie": // kod przechodzi tutaj do jazdy na przednim kole break; } } } }
Funkcja ta wykona odpowiedni rodzaj sztuczki rowerowej w zależności od nazwy, jaką jej przekażesz. Problemem jest to, że gdy popełnisz błąd w nazwie sztuczki, nic się nie wydarzy, co będzie dużym rozczarowaniem. Możesz drapać się po głowie i zastanawiać, gdzie leży błąd, podczas gdy w kodzie nie ma innych błędów poza złym zapisem nazwy sztuczki.
124
Część I Rozpoczęcie pracy z ActionScript 3.0 var stuntBike:Bicycle = new Bicycle(); stuntBike.performTrick("wheely"); // nic się nie wydarzy
Zastąp jednak wszystkie te łańcuchy znaków stałymi: package { public class Bicycle { static public const WHEELIE:String = "wheelie"; static public const BUNNYHOP:String = "bunnyhop"; static public const STOPPIE:String = "stoppie"; public function performTrick(trickName:String):void { switch (trickName) { case Bicycle.WHEELIE: // kod przechodzi tutaj do jazdy na tylnym kole break; case Bicycle.BUNNYHOP: // kod przechodzi tutaj do podskoku break; case Bicycle.STOPPIE: // kod przechodzi tutaj do jazdy na przednim kole break; } } } }
Po zmianie kompilator wychwyci błąd pisowni, ponieważ nie ma zmiennej WHEELY. var stuntBike:Bicycle = new Bicycle(); stuntBike.performTrick(Bicycle.WHEELY); // błąd kompilatora
Jeśli używasz środowiska programistycznego oferującego podpowiedzi dla kodu, jak Flex Builder 3, możesz korzystać z funkcji automatycznego uzupełniania kodu, która interaktywnie pomoże Ci w odnalezieniu poprawnej nazwy sztuczki.
Praktyka ta jest bardzo często wykorzystywana w pakiecie zdarzeń, ale może być stosowana zawsze wtedy, gdy zaistnieje potrzeba porównania wartości z jakimś specjalnym łańcuchem znaków. W tym przypadku możesz pójść jeszcze dalej. Mógłbyś chcieć, żeby sztuczka były definiowana jej własnym typem, który będzie mógł określać jazdę na przednim lub tylnym kole oraz podskok. W tej chwili sztuczka definiowana jest typem String ze specjalnymi wartościami zdefiniowanymi w klasie Bicycle, do której bezsprzecznie nie należą.
Wyliczenia Utworzony własny typ mogący posiadać tylko kilka dyskretnych wartości nazywany jest wyliczeniem. ActionScript 3.0 nie posiada wbudowanej obsługi wyliczeń, ale wyliczenia można bardzo łatwo utworzyć samemu. Możesz utworzyć klasę Trick, która wylicza rodzaje sztuczek obsługiwanych przez rower:
Rozdział 3. Programowanie z wykorzystaniem klas
125
package { public final class Trick { public static const WHEELIE:Trick = new Trick(); public static const BUNNYHOP:Trick = new Trick(); public static const STOPPIE:Trick = new Trick(); } }
Początkowo może to się wydawać dziwne, ale WHEELIE jest nie tylko statyczną właściwością klasy Trick, lecz również instancją klasy Trick. Tworząc instancje sztuczek klasy Trick, a nie tylko łańcuchy znaków przechowywane w Trick, możesz określić typ zmiennych jako Trick. Teraz możesz się upewnić, czy parametr przekazywany do funkcji performTrick() jest rzeczywistą sztuczką, czy może jakimś fałszywym łańcuchem znakowym, który może, ale nie musi być poprawny. Wszystko, co musisz zrobić, to zmienić sygnaturę tej funkcji tak, aby akceptowała parametr typu Trick zamiast parametru typu String. Klasy wyliczeniowe są również idealnymi klasami finalnymi. Ponieważ kod prawdopodobnie jest pisany z założeniem, że wyliczenie może zawierać jedynie pewne wartości, utworzenie podklasy dodającej swoje wartości spowoduje zamieszanie. Jest to jeden z przypadków, gdy modyfikacja własnego kodu jest lepsza od tworzenia podklasy.
Metody statyczne Zmienne i stałe nie są jedynym kodem, który może należeć do klasy. Można też tworzyć metody wykonujące swoje zadania niezależnie od instancji. Te statyczne metody nie muszą uzyskiwać dostępu do żadnych zmiennych instancji, ponieważ uruchomione zostaną przez samą klasę, być może jeszcze przed utworzeniem instancji klasy. Metody statyczne mogą być używane, gdy chcesz uruchamiać jakiś kod, zanim obiekt zostanie rzeczywiście utworzony. W następnym przykładzie zastępujesz zwykły konstruktor metodą statyczną. Możesz użyć tej metody do utworzenia nowych instancji obiektu. package { public class LimitedEdition { private static var editionsMade:int = 0; private static const MAX_EDITIONS:int = 20; private var serialNumber:int; public function LimitedEdition(serialNumber:int) { this.serialNumber = serialNumber; } public static function getOne():LimitedEdition { if (editionsMade++ < MAX_EDITIONS) {
126
Część I Rozpoczęcie pracy z ActionScript 3.0 return new LimitedEdition(editionsMade); } return null; } public function toString():String { return "Obiekt limitowanej serii #" + serialNumber; } } }
Utworzony w powyższym kodzie obiekt należy do limitowanej serii. Może ich zostać utworzonych tylko 20, a podczas tworzenia są one oznakowywane numerami seryjnymi. Zmienna klasowa oraz stała klasowa zostały użyte do śledzenia liczby instancji już utworzonych oraz ogólnej liczby instancji, jakie mogą być utworzone. Ponieważ liczba utworzonych instancji przechowywana jest wewnątrz klasy, nie jest ulotna, potrafi istnieć wewnątrz klasy LimitedEdition niezależnie od tego, czy istnieją jakieś obiekty LimitedEdition. Po utworzeniu docelowej liczby obiektów przewidzianych dla tej serii statyczna metoda tworząca obiekty odmawia zwrócenia kolejnego. Metodę klasową — tak jak i zmienną klasową — wywołuje się poprzez wykorzystanie notacji kropkowej z nazwą klasy. Statyczna metoda getOne() jest funkcją wywoływaną dla klasy, aby ta zwróciła instancję i śledziła, ile instancji LimitedEdition zostało już utworzonych. Natomiast metoda instancji toString() jest wywoływana dla instancji i określa, jak wyglądać będzie Twój obiekt przedstawiony w postaci łańcucha znaków: for (var i:int = 0; i < 50; i++) { var obj:LimitedEdition = LimitedEdition.getOne(); trace(obj.toString()); } /* wydrukuje: Obiekt limitowanej edycji #1 Obiekt limitowanej edycji #2 Obiekt limitowanej edycji #3 Obiekt limitowanej edycji #4 Obiekt limitowanej edycji #5 ... Obiekt limitowanej edycji #19 Obiekt limitowanej edycji #20 null null null null ... null */
Takie podejście nadal ma słabe strony. Pamiętaj, że jednym z wymogów stawianych konstruktorowi jest to, że musi być publiczny. Dlatego też nie możesz zapobiec tworzeniu nowych instancji poprzez ominięcie swojej statycznej metody tworzącej i wywołanie konstruktora. Można to obejść poprzez jakąś sprytną sztuczkę, np. ustawianie flagi z poziomu metody tworzącej i sprawdzanie w konstruktorze, czy flaga ta jest ustawiona. Ważnym
Rozdział 3. Programowanie z wykorzystaniem klas
127
wnioskiem wynikającym z przykładu jest to, że metody statyczne mogą być używane, jeżeli nie ma zdefiniowanej instancji klasy, a metody te mogą pracować z innymi statycznymi, zmiennymi i metodami. Innym powodem stosowania metod statycznych jest tworzenie klas narzędziowych. Klasy narzędziowe nie są wykorzystywane do tworzenia instancji. Istnieją one w celu grupowania powiązanych ze sobą statycznych funkcji. Z tego też powodu klasy statyczne nie są tak naprawdę obiektowe i jeśli za bardzo będziesz na nich polegać, możesz w rezultacie końcowym doprowadzić do tego, że będziesz programował strukturalnie, a nie obiektowo. Przykładem klasy narzędziowej może być klasa Math, którą poznasz w rozdziale 7. Dostarcza ona jednocześnie wielu naprawdę przydatnych funkcji matematycznych, co czyni z niej świetną pomoc. Nigdy nie utworzysz obiektu za pomocą instrukcji new Math(), ale na pewno wywołasz jej statyczną metodę Math.pow (2, 10), aby podnieść 2 do potęgi 10, lub odwołasz się do statycznej stałej Math.PI, gdy będziesz musiał użyć wartości π.
Statyczne metody dostępowe Możesz też tworzyć statyczne niejawne i jawne metody dostępowe. Użyj w tym celu słowa kluczowego static razem z nazwami metod, a będziesz mógł tworzyć statyczne właściwości, które będą wartościami pochodnymi lub tylko do odczytu. Miej jednak na uwadze, że metody statyczne — w tym metody dostępowe — nie mogą wywoływać niestatycznych metod klasy.
Projektowanie interfejsów Rozszerzając klasę podstawową, masz możliwość przesłonięcia dowolnych z jej metod i tym samym stworzenia nowego zachowania. Musisz też odziedziczyć wszystkie istniejące metody i zmienne, które nie są zadeklarowane jako prywatne. Dziedziczenie jest przydatne w przypadku powiększania istniejących klas o nową funkcjonalność, ale ma też swoje wady. Wadą jest konieczność rozpoczynania ze wszystkimi metodami i właściwościami superklasy. Jeśli chcesz, aby rodzina klas wykonywała ten sam rodzaj zadań radykalnie odmiennymi sposobami, konieczność omijania istniejącego kodu może być bardzo uciążliwa. Można obejść ten problem poprzez posiadanie relatywnie pustej klasy bazowej, którą rozszerzać będzie cała Twoja rodzina klas. Wtedy natkniesz się jednak na inny problem: chociaż ta klasa bazowa będzie całkowicie poprawna, to będzie jednocześnie całkowicie nieefektywna. Można by zatem stworzyć instancję pustej klasy bazowej i używać jej w programie, ale tak naprawdę nie daje nam ona żadnych korzyści. Spotkasz się też pewnie z problemem wielokrotnego dziedziczenia. Klasy mogą rozszerzać tylko jedną klasę. Umożliwia to tworzenie hierarchii klas w kształcie drzewa, a nie sieci pajęczej. Pojedyncze dziedziczenie ma sens wtedy, gdy modelujesz według całkowitych, w pełni przemyślanych obiektów: obiekt nie może być i domem, i hamburgerem, ale obiekt może wykonywać dwa różne rodzaje zadań. Drzewo należy zarówno do klasy obiektu
128
Część I Rozpoczęcie pracy z ActionScript 3.0
udzielającego schronienia, jak i klasy obiektu dającego jedzenie. Byłoby dobrze móc używać drzewa w roli dostawcy jedzenia, gdy potrzebujesz jedzenia, oraz jako schronienia, gdy potrzebujesz schronienia. Potrzebny jest więc całkowicie abstrakcyjny element, który klasy mogą implementować na różne sposoby, a przy tym nadaje się on do łączenia z innymi takimi obiektami w jednej klasie. Wszystkie te wymagania spełnia interfejs. Interfejsy są reprezentacjami pewnej zdolności, nie definiują sposobu jej działania. Dostarczają natomiast informacji odnośnie do sposobu jej wykorzystania. Interfejsy nie są klasami i nie można tworzyć ich instancji. Nie zawierają kodu. Interfejsy są kontraktami. Gdy zdecydujesz się na implementację interfejsu, jego realizacja będzie zależała całkowicie od Ciebie, ale musisz spełnić jego specyfikacje. Jeżeli dziedziczenie jest relacją „jest”, a kompozycja jest relacją „posiada”, to implementacja interfejsu jest relacją „potrafi”. Wyjaśnimy to na przykładzie. Ten interfejs byłby zapisany w pliku nazwanym IPowerable.as: package { public interface IPowerable { function turnOn(volts:Number):void; function turnOff():void; function getPowerUse():Number; } }
W ActionScript 3.0 adnotacje do typu — dodatki do nazwy elementu wskazujące jego zastosowanie lub typ — nie są wymagane i zazwyczaj się ich nie używa. Zwyczajowo jednak dodaje się przed nazwami interfejsów literę I, jak np. IPowerable.
Dla metod definiowanych w interfejsie nie jest wymagany modyfikator dostępu, ponieważ to interfejsy definiują publiczny interfejs dla obiektu. Metody te muszą z definicji być metodami publicznymi. Interfejsy nie mogą określać właściwości, ale mogą określać funkcje jawnych lub niejawnych metod dostępowych. Interfejs IPowerable zapewnia pewnego rodzaju zachowania. Określa, jak rzeczy, które mogą być zasilane energią, będą współpracować ze światem zewnętrznym. Interfejs określa kontrakt, którego weryfikacja w klasach chcących spełniać wymagania interfejsu IPowerable realizowana jest przez kompilator. Klasy implementujące interfejs IPowerable nie muszą ograniczać się do wymienionych wyżej trzech metod, ale muszą posiadać własną implementację tych trzech metod. Zestaw obiektów pobierających energię może cechować się bardzo różnymi zachowaniami. Np. lampa pobiera energię elektryczną i wytwarza światło: package { public class Lamp implements IPowerable { private var watts:Number; private var isOn:Boolean;
Rozdział 3. Programowanie z wykorzystaniem klas
129
public function Lamp(wattage:Number) { watts = wattage; isOn = false; } public function turnOn(volts:Number):void { isOn = true; trace("robi się jaśniej!"); } public function turnOff():void { isOn = false; trace("robi się ciemniej."); } public function getPowerUse():Number { return (isOn)? watts : 0; } } }
Klasa Lamp spełnia kontrakt ustanowiony przez interfejs IPowerable poprzez zadeklarowanie trzech metod interfejsu z identycznymi sygnaturami metod. Nazwy parametrów są nieistotne dla sygnatury metody, ale należy zachować odpowiednie typy, porządek oraz liczbę parametrów. Słowo kluczowe implements jest używane do uczynienia klasy przynależną do określonych interfejsów. Klasa może jednocześnie rozszerzać inną klasę oraz implementować interfejs, ale sekcja extends musi wystąpić przed sekcją implements. Obiekty implementujące interfejs możesz tworzyć na bardzo różne sposoby. Np. toster nie pobiera energii, dopóki nie włożysz do niego chleba i nie rozpoczniesz podpiekania. O ile obiekt potrafi wykonać turnOn(), turnOff() oraz getPowerUse(), to spełnia konieczne warunki interfejsu IPowerable. package { public class Toaster implements IPowerable { private var isOn:Boolean; private var isToasting:Boolean; public function turnOff():void { isOn = false; isToasting = false; } public function getPowerUse():Number { if (isToasting) { return 100; } else { return 0; }
130
Część I Rozpoczęcie pracy z ActionScript 3.0 } public function turnOn(volts:Number):void { isOn = true; } public function toast(pieceOfBread:Bread):Bread { if (!isOn) { trace("nic się nie dzieje); return pieceOfBread; } trace("Twój chleb jest podpiekany"); isToasting = true; pieceOfBread.toastiness += 10; return pieceOfBread; } } }
Jeśli chcemy używać klas Lamp oraz Toaster, to tworzymy je i manipulujemy nimi jak zwykłymi klasami. Jednak niektóre inne klasy „nie przejmują się” tym, jakim typem urządzenia jest obiekt, o ile implementuje on interfejs IPowerable. System elektryczny jest również interfejsem, ponieważ tak jak interfejs określa warunki dla obiektów, które chcą go używać. Na przykład w Polsce po włożeniu do kontaktu wtyczki zakończonej dwoma bolcami uzyskuje się prąd przemienny o napięciu 220 woltów i częstotliwości 50 Hz. Jeśli dodatkowo gniazdko wyposażone jest w bolec uziemiający i wtyczka posiada odpowiedni otwór na ten bolec, to połączenie jest uziemione. Jest to pewien rodzaj kontraktu, a w przypadku drobnych odstępstw od tego kontraktu ze strony dostawców i koncernów elektrycznych definitywnie „usmażyłoby się” wszystko, co posiadasz w swoim gospodarstwie domowym. Ponieważ ten interfejs znajduje się na miejscu, możesz użyć dowolnego urządzenia elektrycznego i pobierać energię poprzez zwykłe włożenie wtyczki do kontaktu. Drobne uzgodnienia — jak np. setki protokołów używanych dzisiaj w internecie, znany wszystkim sposób naciskania przycisków i stukania w klawisze, spodziewany jednolity sposób obsługi wszystkich rodzajów samochodów — są powszechne na całym świecie. Interfejsy umożliwiają systemom wspólną pracę w oparciu o pewne oczekiwania względem siebie, a nie o bieżące implementacje tych systemów. Mając to na uwadze, powinniśmy być w stanie utworzyć listwę zasilającą, która zdoła zasilić wszystkie Twoje urządzenia jednocześnie. Dla listwy zasilającej nie ma znaczenia, co do niej podłączysz, o ile to, co do niej podpinasz, jest zgodne z interfejsem IPowerable. Lampy i tostery mogą pochodzić z całkowicie różnych rodzin klas, ale dzięki interfejsom możemy traktować obiekt w kontekście na co się zgadza, a nie czym jest. package { public class PowerStrip implements IPowerable { private var appliances:Array; public function PowerStrip() { appliances = new Array(); } public function addAppliance(appliance:IPowerable):void
Rozdział 3. Programowanie z wykorzystaniem klas
131
{ appliances.push(appliance); } public function turnOn(volts:Number):void { for each (var appliance:IPowerable in appliances) { appliance.turnOn(volts); } } public function turnOff():void { for each (var appliance:IPowerable in appliances) { appliance.turnOff(); } } public function getPowerUse():Number { var powerDraw:Number = 0; for each (var appliance:IPowerable in appliances) { powerDraw += appliance.getPowerUse(); } return powerDraw; } } }
Ponadto listwa zasilająca sama w sobie powinna być zasilana (implementować interfejs IPowerable), abyśmy mogli zamknąć wszystkie urządzenia w jednym obiekcie. Jeśli chodzi o system elektryczny, urządzenia w Twoim domu są niczym jedno wielkie urządzenie. Jest to coś wspaniałego: możesz gromadzić wiele obiektów w jednym i używać tego jednego obiektu, niezależnie od szczegółów jego elementów składowych. Czy w prawdziwym życiu nie jest wygodniej po prostu wyłączyć listwę zasilającą, zamiast wyłączać po kolei wszystkie podłączone do niej urządzenia? Zauważ, że we wszystkich pętlach for..each oraz w funkcji addAppliance() używasz jako typu IPowerable. Dopóki zgodzisz się na implementowanie przez obiekt interfejsu IPowerable i ograniczać się będziesz do możliwości gwarantowanych konkretnie przez interfejs IPowerable, nie powinno być żadnych problemów. To właśnie mamy na myśli, gdy mówimy o programowaniu w kontekście zdolności, a nie klas. Teraz utwórz kilka urządzeń i przetestuj listwę zasilającą: var powerStrip:PowerStrip = new PowerStrip(); var heater:Heater = new Heater(10); var toaster:Toaster = new Toaster(); var readingLamp:Lamp = new Lamp(25); var overheadLamp:Lamp = new Lamp(60); powerStrip.addAppliance(heater); powerStrip.addAppliance(toaster); powerStrip.addAppliance(readingLamp); powerStrip.addAppliance(overheadLamp);
132
Część I Rozpoczęcie pracy z ActionScript 3.0 toaster.toast(new Bread()); // nic się nie dzieje powerStrip.turnOn(120); // Robi się cieplej! Robi się jaśniej! Robi się jaśniej! trace(powerStrip.getPowerUse()); // 1285 toaster.toast(new Bread()); // Twój chleb jest podpiekany trace(powerStrip.getPowerUse()); // 1385 powerStrip.turnOff(); // Robi się zimno. Robi się ciemniej. Robi się ciemniej.
Zwróć uwagę na to, że pomimo iż zmienna toaster spełnia kontrakt interfejsu IPowerable, jest typu Toaster i w dalszym ciągu może używać metod specyficznych dla klasy Toaster, jak toast(). Poprzez implementację interfejsu nie tracisz niczego. Klasa może implementować wiele interfejsów, jak we wcześniejszym przykładzie z drzewem. Aby zaimplementować wiele interfejsów, wystarczy rozdzielić nazwy interfejsów przecinkami. Przykład z drzewem wyglądałby zatem tak: public class Tree extends Plant implements IShelterProvider, IFoodProvider
Nazwy interfejsów często kończą się na -able (z ang. „zdolny do”), ponieważ opisują zdolność do czegoś. Jednak trzymanie się tej nomenklatury może często prowadzić do dziwacznych nazw interfejsów, dlatego zalecamy porzucenie tej reguły w takich właśnie sytuacjach.
Interfejsy mogą również rozszerzać inne interfejsy. Możesz wykorzystać to np. do utworzenia kategorii obiektów, które oprócz tego, że posiadałyby zdolności specyficzne dla tej kategorii, mogłyby również być źródłem zdarzeń. Mógłbyś to zrealizować poprzez rozszerzenie wbudowanego interfejsu IEventDispatcher. Więcej na temat źródeł zdarzeń dowiesz się w części IV. Programiści obiektowi często wykorzystują interfejsy, ponieważ ogranicza to zależności pomiędzy konkretnymi klasami. Im więcej zależności stworzysz, tym mniejszą pozostawiasz sobie możliwość zmiany programu bez wpływania na istniejący kod, który wraz z potęgowo powiększającą się pajęczyną zależności będzie się stawał coraz mniej trwały. Jak już wspominaliśmy, interfejsy umożliwiają Ci adresowanie obiektów w kontekście nie tego, czym są, ale w kontekście minimum, jakie muszą dla Ciebie wykonać, a to może dać Ci precyzyjną kontrolę nad sposobem obsługiwania obiektów. Ponieważ obiekty mogą implementować wiele interfejsów, mogą być czym innym dla jednej klasy i czym innym dla innej klasy, zależnie od potrzeb. Podstawowa reguła programowania obiektowego brzmi: programuj dla interfejsu, nie dla implementacji. Słowo „interfejs” niekoniecznie musi tu oznaczać słowo kluczowe interface, może też oznaczać superklasę lub cokolwiek, co będzie najbardziej ogólnym typem dającym Ci możliwości, których w danej chwili potrzebujesz. Implementacje się zmieniają, ale interfejsy nie powinny: są teraz laptopy, w których istnienie nie uwierzono by przed 10 laty. Gdyby jednak dzisiaj można było przenieść się w czasie, możliwe byłoby zabranie ich ze sobą i zaprezentowanie ich sposobu działania, ponieważ interfejs korzystania z energii elektrycznej nie uległ zmianie. Nie zmienił się także sposób pisania na klawiaturze i oglądania obrazu na ekranie.
Rozdział 3. Programowanie z wykorzystaniem klas
133
Manipulowanie typami System typów umożliwia odwoływanie się do obiektu poprzez jego klasę, poprzez jedną z jego superklas, poprzez interfejs, który implementuje, lub poprzez ich kombinację. Wszystkie z tych potencjalnych typów dla obiektu są poprawne, ponieważ będąc bardziej specyficznym, musi implementować funkcjonalność zestawu wszystkich bardziej ogólnych typów. Square nadal jest Rectangle, ponieważ jest specyficznym typem Rectangle, i jest też Shape, ponieważ jest bardzo specyficznym rodzajem Shape. Może też być IPointCollection, jeśli posiada zestaw punktów będących jego wierzchołkami. Ważne jest to, aby nie były to konwersje; klasa naprawdę jest wszystkim tym, co rozszerza. Ponieważ możesz odwoływać się do klasy poprzez wiele nazw, potrzebujesz sposobu rozróżniania typów. Powinieneś być w stanie testować w czasie wykonywania programu, czy obiekt jest instancją danej klasy, czy implementuje konkretny interfejs. Powinieneś mieć możliwość przekształcenia specyficznego typu w typ ogólny, a także móc spróbować przekształcić typy ogólne w typy specyficzne. Powinna też istnieć możliwość próbowania przekształcenia typów na typy niepowiązane.
Zgodność i koercja typów Niektóre typy mogą być konwertowane bez żadnej jawnej operacji. Ponieważ każda specyficzna klasa jest również bardziej ogólną klasą, możesz przekonwertować ją na bardziej ogólny typ bez pisania jakiegoś jawnego kodu. var square:Rectangle = new Square();
W tym przypadku znowu nie mamy do czynienia z konwersją, a ze zmianą nazwy. Te typy są ze sobą zgodne. Square jest Rectangle. Oprócz tego występują jeszcze pewne konwersje niepowiązanych typów, które ActionScript 3.0 potrafi przeprowadzać dla Ciebie bez pytania Cię o zgodę. Nazywa się to koercją lub niejawną konwersją typu. ActionScript 3.0 potrafi konwertować wszystko na typ Boolean, umożliwiając np. wykonywanie następujących sztuczek: var array:Array = ["witaj", "świecie"]; var obj:Object; // Weź wartość z początku tablicy, ustaw obj na tę wartość // i sprawdź, czy obj przekonwertowana zostanie na prawdę czy fałsz. Zakończ pętlę, gdy // wartość przekonwertowana zostanie na fałsz. Null jest zwracana przez shift(), gdy // w tablicy nie będzie już wartości. Null jest konwertowana na fałsz. while (obj = array.shift()) { trace(obj); } // witaj // świecie var str:String = ""; // Ustaw domyślny łańcuch znaków, jeśli łańcuch nie istnieje lub jest pusty. // Zarówno puste łańcuchy znaków, jak i wartości null są konwertowane na fałsz. // Zanegowanie fałszu daje prawdę, zatem instrukcja warunkowa wykona się, // jeśli łańcuch znaków jest pusty, i nada mu domyślną wartość. if (!str) str = "Domyślna";
134
Część I Rozpoczęcie pracy z ActionScript 3.0 trace(str); // Domyślna for (var i:int = 1; i < 4; i++) { // Wypisz liczbę (i) oraz czy jest parzysta czy nieparzysta. // i % 2 jest resztą z dzielenia przez 2, zawsze liczbą całkowitą. // Każda liczba konwertowana jest na prawdę poza 0, // które konwertowane jest na fałsz. Zatem gdy reszta z dzielenia // przez dwa wynosi zero, warunek jest fałszywy. trace(i, (i % 2)? "nieparzysta" : "parzysta"); } // 1 nieparzysta // 2 parzysta // 3 nieparzysta
Pierwsza sztuczka pokazuje, że każdy obiekt daje true, ale wartość null daje false. Druga sztuczka pokazuje, że pusty łańcuch znaków daje false. Trzecia pokazuje, że liczba całkowita daje false, gdy ma wartość 0, a w pozostałych przypadkach daje true. ActionScript 3.0 używa metody toString() do automatycznej konwersji dowolnego obiektu na typ String. W przypadku gdy klasa obiektu ani żadna inna klasa niebędąca wyżej w hierarchii nie definiuje metody toString(), wykorzystywana jest metoda toString() z klasy Object. W rozdziale 9. znajdziesz więcej informacji na temat klasy Object. Inne niejawne konwersje dostępne są dla bazowych typów ActionScript 3.0, ale omawiane są w rozdziałach zajmujących się tymi typami. Np. XML może zostać automatycznie przekonwertowany na typ String, gdy użyty zostanie w kontekście tekstowym.
Jawna konwersja typu Możesz też sam dokonać konwersji typu. ActionScript 3.0 oferuje dwa rodzaje rzutowania, które umożliwiają przeprowadzenie konwersji z jednego typu na drugi. Rzutowanie próbuje zmieścić typ w innym typie. Jeśli są ze sobą zgodne, konwersja będzie kontynuowana, a wyrażenie da w rezultacie nowy typ. Typy rzutowania różnią się zachowaniem w sytuacji, gdy konwersja się nie powiedzie. Innym elementem terminologii jest kierunek rzutowania. Rzutowanie w górę jest konwersją typu w górę łańcucha dziedziczenia, zmierzającą do ogólności. Ten rodzaj rzutowania zawsze kończy się powodzeniem i jak się przekonałeś, może zostać przeprowadzony niejawnie, bez operacji rzutowania. Rzutowanie w dół jest konwersją typu w dół łańcucha dziedziczenia, w kierunku większej specyfikacji. Ponieważ nie masz gwarancji, że konkretny Reactangle jest Square, a spróbujesz rzutować go bez wnikliwego przyjrzenia się mu, to ryzykujesz, że rzutowanie się nie powiedzie. Zademonstrujemy rzutowanie na przykładzie rzutowania w dół, ponieważ w jego przypadku możliwe jest niepowodzenie. W przypadku ewentualnego niepowodzenia będziesz mógł zobaczyć, czym różnią się typy rzutowania. Pierwszy rodzaj rzutowania, jaki możesz przeprowadzić, jest bezpieczniejszy i zalecany w większości przypadków. Aby rzutować zmienną na typ, ujmij nazwę instancji w nawiasy i umieść przed nimi nazwę typu, tak jakbyś wywoływał jego konstruktor, ale bez operatora new: var someShape:Shape = getRandomShape(); // weź jakiś nieznany kształt Square(someShape); // traktuj go jako kwadrat
Rozdział 3. Programowanie z wykorzystaniem klas
135
Rysunek 3.9. Rzutowanie w górę i w dół
W powyższym przykładzie ze względu na argumentację zakładamy, że uzyskałeś kształt, ale nie wiesz, jaki to typ kształtu. Może się tak przydarzyć, gdy metoda zwraca klasę ogólną zamiast specyficznej. Reprezentuje to tutaj wywołanie metody getRandomShape(). Jeżeli konwersja się nie powiedzie — czyli gdy someShape będzie w rzeczywistości prostokątem o różnych wartościach dla wysokości i szerokości — to rzutowanie spowoduje wystąpienie błędu TypeError (błąd typu). Aby dowiedzieć się więcej na temat obsługiwania takich błędów oraz dlaczego należy je traktować jak swoich przyjaciół, zajrzyj do rozdziału 19. W celu przechwycenia wyniku tego rzutowania prawdopodobnie będziesz chcieć przechować wynik rzutowania w zmiennej o typie, do którego rzutujesz: var mySquare:Square = Square(someShape);
Drugi rodzaj rzutowania nie zgłasza błędu, gdy rzutowanie się nie powiedzie, ale zwraca wartość null. Błąd jest zazwyczaj bardziej pożądaną informacją, ponieważ podczas testowania programu zatrzymuje program w miejscu niepowodzenia rzutowania, a nie gdzieś dalej, gdy nieoczekiwana wartość null spowoduje wystąpienie jakichś innych błędów w innych miejscach kodu, stając się tym samym trudną do wykrycia pluskwą. Taki rodzaj rzutowania przeprowadza się poprzez użycie operatora as, który powoduje traktowanie obiektu z lewej strony operatora tak, jakby był klasą z prawej strony. var mySquare:Square = someShape as Square;
Jednym z powszechnych zastosowań rzutowania jest pobieranie obiektów ze zbioru. Ponieważ ActionScript 3.0 nie obsługuje szablonów lub typów generycznych, zbiory takie jak
136
Część I Rozpoczęcie pracy z ActionScript 3.0 We wcześniejszych wersjach ActionScript możliwe było jedynie rzutowanie podobne do pierwszego sposobu, jednak zachowywało się jak rzutowanie z użyciem słowa kluczowego as w ActionScript 3.0, ponieważ zwracało wartość undefined, gdy konwersja typu się nie powiodła. W AS3 nowe rzutowanie ze słowem as zwraca null, a rzutowanie w stylu konstruktora zwraca błąd.
tablice po prostu przechowują elementy typu Object, a podczas pobierania wartości z takich zbiorów zwracane są one jako typ Object, ponieważ ActionScript nie może gwarantować za zawartość tablicy. Przechodząc zatem w pętli po elementach takiego zbioru, możesz rzutować te elementy na klasę, z której oryginalnie się wywodzą. Ponieważ podczas pobierania tych elementów są one typu Object, zawsze będzie to rzutowanie w dół, chyba że przechowywałeś w tablicy instancje klasy Object. Inną korzyścią płynącą ze stosowania rzutowania w stylu konstruktora jest to, że korzysta ono z kilku inteligentnych funkcji konwertujących, sprytnie przebranych za operatory rzutowania. Najwyższy poziom ActionScript 3.0 zawiera pomiędzy pozostałymi funkcjami najwyższego poziomu, jak trace() czy isNan(), wiele funkcji, które podczas ich używania cechują się dokładnie tą samą składnią, co rzutowanie. Te funkcje zachowują się też jak rzutowanie, ale zamiast nie radzić sobie z konwersją niepowiązanych typów, przeprowadzają konwersję inteligentną. Np. funkcja XML() wygląda jak rzutowanie na XML, ale przekonwertuje tekst XML w obiekcie typu String na rzeczywisty obiekt XML, podczas gdy rzutowanie zakończyłoby się tutaj niepowodzeniem. var myXml:XML = XML("TerazTutaj"); trace(myXml.party[0].time); // Teraz
Ponieważ funkcje te zachowują się jak rzutowanie (ale są bardziej inteligentnie) i przebrane są za operatory rzutowania, to wiedząc o tym, że istnieją, możesz błogo je ignorować. Stosując rzutowanie w stylu konstruktora, automatycznie skorzystasz z kilku funkcji konwertujących.
Określanie typów Aby określić, czy obiekt jest kompatybilny z klasą, możesz użyć operatora is. Jest to dwuargumentowy operator, a nie funkcja, stąd składnia dla jego zastosowania wygląda następująco: // pobierz kształt z dowolnego miejsca programu var someShape:Shape = getRandomShape(); if (someShape is Circle) { (someShape as Circle).circumference; }
W tym przykładzie tylko koła posiadają obwód, dlatego jeżeli możesz ustalić, że kształt to Circle, możesz następnie bezpiecznie pobrać jego obwód. Ponieważ właściwość circum ference jest definiowana jedynie w instancjach Circle, to nawet jeśli wiesz, że instancja someShape w rzeczywistości jest Circle, przed uzyskaniem dostępu do właściwości Circle musisz przekonwertować jej typ na Circle.
Rozdział 3. Programowanie z wykorzystaniem klas
137
Ponieważ sprawdziłeś, czy instancja jest typu Circle, nie było niebezpieczeństwa niepowodzenia podczas rzutowania w dół, gdyż sprawdziłeś już zgodność jej typu w wierszu powyżej. Operator is jest nowością w ActionScript 3.0 i zastępuje operator instanceof używany w ActionScript 2.0.
Należy pamiętać o tym, że is określa kompatybilność typu, a nie dokładny typ instancji. var s:Shape = new Square(); trace(s is Square); // true trace(s is Rectangle); // false
Możesz zrobić znacznie więcej w przypadku typu obiektu, np. określić dokładny typ instancji i uzyskać referencję do klasy po samej nazwie klasy, ale te zagadnienia, ogólnie nazywane refleksją, są zaawansowanymi technikami, których nie opisujemy w tym rozdziale.
Tworzenie klas dynamicznych Ostatni rodzaj klas zachowaliśmy na sam koniec, ponieważ powinno się go używać rzadko i ostrożnie. Dynamiczne klasy to takie klasy, których właściwości i metody mogą być zmieniane, dodawane i usuwane w czasie wykonywania. Działa to wbrew systemowi typów i wielu reguł programowania obiektowego, jednak w niektórych okolicznościach może być wygodne. Klasę dynamiczną możesz utworzyć poprzez dodanie słowa kluczowego dynamic do definicji klasy. public dynamic class ShapeShifter
Korzyści wynikające ze stosowania klas dynamicznych i właściwości tych klas omawiamy w rozdziale 9., który poświęcony jest klasie Object, czyli uwielbianej przez wszystkich klasie dynamicznej. W większości przypadków, w których potrzebował będziesz klasy dynamicznej, być może zadowolisz się instancją Object zamiast instancją własnej klasy dynamicznej.
Podsumowanie
Klasy są szablonami dla obiektów, elementami składowymi Twojego programu.
Obiekty zawierają dane oraz operacje.
Klasy powinny zawierać w sobie swoje implementacje i oferować prosty interfejs.
Klasy są umieszczane w pakietach tworzących strukturę i umożliwiających im posiadanie unikalnych nazw.
Obiekty mają typ będący klasą, której są instancją.
Wszystkie zmienne są obiektami, nawet liczby i wartości boolowskie.
138
Część I Rozpoczęcie pracy z ActionScript 3.0
Obiekty zawierają metody i właściwości, a klasy mogą zawierać statyczne metody i zmienne.
Możesz używać modyfikatorów dostępu public, private, protected, internal oraz własnych przestrzeni nazw w celu enkapsulacji oraz sterowania dostępem do swoich obiektów.
Klasy mogą dziedziczyć po innych klasach, automatycznie przejmując ich publiczne i chronione właściwości i metody.
Klasy mogą dziedziczyć tylko z jednej klasy, dzięki czemu wszystkie klasy można narysować w postaci drzewa. Każda klasa może posiadać łańcuch dziedziczenia sięgający aż do szczytu drzewa.
Klasy powinny być zamknięte na modyfikację, a otwarte na rozszerzanie.
Publiczny interfejs klasy składa się z jej publicznych metod i właściwości. Definiuje on postać klasy widoczną dla świata zewnętrznego.
Interfejsy definiują kontrakt, który klasy mogą chcieć spełniać w celu oferowania jakiejś funkcji.
Możesz używać interfejsów jako typów, co umożliwi Ci oddzielenie kodu od implementacji.
Typy można sprawdzać, automatycznie wymuszać lub jawnie rzutować.
Rozdział 4.
Praca z metodami i funkcjami W tym rozdziale:
Ponowne wykorzystanie bloków kodu poprzez definiowanie własnych funkcji
Wywoływanie funkcji i przekazywanie argumentów za pomocą operatora wywołania
Otrzymywanie wyników poprzez zwracanie wartości z funkcji
Praca z funkcjami jako obiektami
Tworzenie funkcji rekurencyjnych
Teraz, gdy wiesz już wszystko na temat zmiennych, zapewne będziesz chciał coś z nimi zrobić. W tym miejscu do akcji wkraczają metody i funkcje. Funkcje są blokami kodu wielokrotnego zastosowania, które mogą być definiowane w Twoich własnych klasach i często są stosowane w całym API ActionScript 3.0. Funkcje umożliwiają organizowanie kodu w niezależne fragmenty. Mogą być używane do zwracania różnych wyników w zależności od dostarczonych im danych. Być może najważniejsze jest jednak to, że funkcje mogą enkapsulować wewnątrz klasy zachowania i funkcjonalność oraz oferować publiczny interfejs dla tej funkcjonalności. W niniejszym rozdziale opisano sposoby wykorzystywania funkcji i tworzenia od podstaw własnych funkcji. Jedną z rzeczy, jakie należy zapamiętać w związku z ActionScript, jest to, że każda zmienna i część klasy jest obiektem. Funkcje nie są tutaj wyjątkiem. Chociaż może być to trudne do wyobrażenia, funkcje są instancjami klasy Function i zawierają własne metody oraz właściwości. W tym rozdziale opisujemy, jak używać funkcji jako obiektów.
Funkcje Mówiąc najprościej, funkcja jest fragmentem kodu zachowanym w postaci zapisanej procedury, która może być uruchomiona w razie potrzeby poprzez wpisanie nazwy funkcji lub wywołanie funkcji. Każdy napisany przez Ciebie program będzie w dużym stopniu bazował na funkcjach.
140
Część I Rozpoczęcie pracy z ActionScript 3.0
Różnice pomiędzy metodami i funkcjami Będziesz spotkać się ze słowami „metoda” oraz „funkcja” (a czasami nawet z określeniem „domknięcie funkcji”) oznaczającymi blok kodu nadający się do wielokrotnego zastosowania. Pomimo tego iż często nazw tych używa się zamiennie, występuje pomiędzy nimi niewielka różnica. Metoda jest dowolną funkcją zdefiniowaną wewnątrz klasy. Metody powinny być logicznie powiązane z instancją klasy i dążyć do spójności z obiektem, w którym są zdefiniowane. Np. klasa Kitty może definiować funkcję o nazwie meow(). W tym przypadku meow() będzie metodą klasy Kitty. Ogólnie pojęcia „metoda” oraz „funkcja” różnią się więc tylko nazwami — innymi słowy, metody nie posiadają żadnych dodatkowych możliwości, nie występuje też słowo kluczowe „method”. Oto przykład wywołania wymyślonej funkcji: addElementToGroup(myGroup, myElement);
A teraz wywołanie tej samej funkcji, gdy napisana będzie jako metoda: myGroup.addElement(myElement);
Jak widzisz, powyższe wywołania są do siebie podobne, ale drugie zostało tak zaprojektowane, aby operować na instancji myGroup. W tej książce używamy obu terminów. Używamy słowa „funkcja” dla zastosowania ogólnego, a podczas opisywania funkcjonalności konkretnej funkcji zdefiniowanej wewnątrz klasy używamy pojęcia „metoda”. Ponieważ prawie wszystkie funkcje w ActionScript znajdują się w klasach, większość z nich jest metodami. Inaczej niż w AS2, który czasami wykorzystuje klasę Delegate, w AS3 metody są powiązane z instancjami, w których się znajdują. Wykonają się tak, jakby zostały wywołane z instancji, nawet wtedy gdy funkcja zostanie przekazana do innego zasięgu. Ta właściwość ActionScript 3.0 znana jest jako domknięcie metody.
Wywołanie funkcji Wykonanie kodu funkcji lub metody znane jest jako wywołanie lub uruchamianie. Funkcje w ActionScript są wywoływane za pomocą nazwy funkcji, po której występują nawiasy (). Oficjalnie nawiasy te nazywane są operatorem wywołania. Dodatkowe informacje mogą być przekazywane do Twoich funkcji poprzez dodanie argumentów (znanych także jako parametry) umieszczanych między nawiasami i rozdzielanych przecinkami. Niektóre funkcje będą posiadać argumenty opcjonalne lub wymagane. Liczba i typ wartości przekazywanych do funkcji musi odpowiadać definicji funkcji, zwanej także sygnaturą metody. Jeszcze inne funkcje nie wymagają wcale argumentów, a w ich wywołaniu używa się pustej pary nawiasów. Poniżej widzisz dwa poprawne wywołania funkcji: trace("Witaj świecie!"); addChild(mySprite);
Rozdział 4. Praca z metodami i funkcjami
141
Nie zapomnij o dodaniu nawiasów podczas wywoływania funkcji. W przeciwnym razie wyznaczona zostanie wartość funkcji jako zmiennej (obiektu typu Function) zamiast wykonania się kodu wewnątrz funkcji, co doprowadzi do nieoczekiwanych wyników. W dalszej części tego rozdziału dowiesz się, jak i kiedy używać funkcji bez operatora wywołania.
Wywoływanie funkcji a wywoływanie metod Aby uzyskać dostęp do metod wewnątrz konkretnej instancji, użyj nazwy obiektu zawierającego metody, a po niej umieść kropkę i wywołanie metody. Taki sposób wywołania nazywa się notacją kropkową. Możesz traktować to jak wywołanie funkcji w kontekście obiektu. var nameList:Array = new Array(); nameList.push("Daniel", "Julia", "Paweł"); nameList.reverse(); trace(nameList); // Wyświetli: "Paweł", "Julia", "Daniel"
Niektóre instrukcje ActionScript lub operatory, jak typeof czy delete, nie są technicznie funkcjami, nawet jeśli tak się zachowują. Z tego też powodu nie będziesz musiał dodawać nawiasów wokół argumentów dla tych poleceń i nie będziesz w stanie uzyskać dostępu do metod i właściwości klasy Function dla tych zarezerwowanych słów. Operatory te, w przeciwieństwie do większości funkcji, istnieją globalnie i będą dostępne w dowolnym miejscu kodu.
Tworzenie własnych funkcji Aby utworzyć własne metody, należy dodać funkcje do swoich plików przechowujących deklaracje klas. Nazywa się to definiowaniem lub deklarowaniem funkcji. Przyjrzyjmy się tej operacji.
Definiowanie funkcji Funkcje posiadają następującą strukturę: public function doSomething(arg1:Object, arg2:Object):void { // wykonywany kod znajduje się tutaj }
Rozbijmy to na poszczególne człony: — modyfikator dostępu będący zazwyczaj słowem kluczowym, takim jak public, private czy internal, używany jest do określania dostępu do tej metody dla innych klas. Jeśli definiujesz funkcję poza pakietem (np. na osi czasu w programie Flash CS3), powinieneś pominąć tę część. Zawsze powinieneś definiować przestrzeń nazw dla swoich metod. W celu uproszczenia niektóre przykłady w tej książce mogą pomijać tę część.
public
— następnie występuje słowo kluczowe function. Jest ono zawsze wymagane podczas definiowania funkcji, podobnie jak słowo var wymagane jest podczas definiowania zmiennej.
function
142
Część I Rozpoczęcie pracy z ActionScript 3.0
— bezpośrednio po słowie kluczowym function występuje nazwa funkcji. Jest ona zarazem poleceniem, jakie wywołasz, gdy zechcesz wykonać daną funkcję.
doSomething
Najlepsze są opisowe nazwy funkcji. Opisują akcję i mogą być czytane z łatwością, jakbyś czytał zdanie. Niepisana zasada mówi, że nazwa funkcji powinna zaczynać się od czasownika. Np. nazwa button() nie jest tak wyrazista jak drawButton(). Idąc tym tropem, najlepszymi nazwami dla zmiennych są rzeczowniki. Możesz więc łączyć czasownik z rzeczownikami i tworzyć krótkie zdania, jak snakeHandler.feed(python, mouse). (arg1:Object, arg2: Object) — za nazwą funkcji występuje rozdzielona przecinkami
lista argumentów, znajdująca się pomiędzy parą nawiasów. Po każdym argumencie powinien wystąpić dwukropek oraz typ danych tego argumentu. — za nawiasami występuje kolejny dwukropek i typ danych dla wartości zwracanej przez funkcję. Jest to wartość, jaka zostanie zwrócona przez funkcję po zakończeniu jej wykonywania. W tym przypadku zwracany typ to void, ponieważ funkcja nie posiada instrukcji zwracającej. Więcej na temat typów zwracanych dowiesz się w dalszej części tego rozdziału.
:void
— cały kody wykonywany po wywołaniu funkcji znajduje się pomiędzy dwoma nawiasami klamrowymi {}. Nazywa się go ciałem funkcji.
{...}
Wszystkie funkcje wymagają słowa kluczowego function, nazwy funkcji, nawiasów i ciała funkcji. Pozostałe obszary nie są wymagane, co nie oznacza, że nie powinieneś ich używać.
Przekazywanie parametrów do funkcji Funkcje stają się o wiele bardziej interesujące i bardziej przydatne, gdy dostarczysz im jakichś danych zewnętrznych. Można to osiągnąć poprzez dodanie do Twojej definicji funkcji argumentów, nazywanych również parametrami. Aby tego dokonać, po prostu umieść jeden lub więcej argumentów w nawiasach w instrukcji funkcji. Nazwy, jakie tutaj zdefiniujesz, będą dostępne w trakcie wykonywania jako zmienne lokalne i będziesz mógł ich używać podczas wykonywania kodu. Nie wszystkie funkcje będą wymagać parametrów. Niektóre będą wywoływane z pustymi nawiasami. Przyjrzyj się przykładowi wyznaczającemu obwód okręgu, w którym jako parametr przekazywana jest średnica okręgu: function getCircumference(diameter:Number):Number { return Math.PI * diameter; }
Jak widzisz, parametry te mogą być ponownie zapisywane i istnieją w lokalnym zasięgu funkcji. Oznacza to, że przesłaniają zmienne posiadające te same nazwy i istniejące w zasięgu klasy lub zasięgu globalnym.
Rozdział 4. Praca z metodami i funkcjami
143
W trybie standardów liczba argumentów musi odpowiadać definicji funkcji. Różni się to od podejścia stosowanego w poprzednich wersjach ActionScript, gdzie symetria pomiędzy argumentami w definicji funkcji oraz wywołaniem funkcji nie była wymagana.
Przekazywanie przez referencję lub przez wartość W kontekście przekazywania wartości do funkcji ActionScript 3.0 traktuje proste oraz złożone typy danych w odmienny sposób. Proste typy danych są przekazywane przez wartość — czyli ich wartość jest kopiowana do funkcji, pozostawiając oryginał nietknięty, niezależnie od tego, co wydarzy się wewnątrz funkcji. Złożone typy danych, jak Array, są przekazywane przez referencję, co powoduje przekazanie oryginalnego obiektu zamiast duplikatu. Reguły te stosuje się w ten sam sposób podczas przypisywania wartości zmiennym. Jeżeli używasz komputera, a jestem pewien, że większość z Czytelników tej książki go używa, na pewno znasz różnicę pomiędzy kopiowaniem plików a tworzeniem skrótów (lub aliasów w Mac OS). Zgodnie z tym, co pokazano na rysunku 4.1, przekazywanie poprzez wartość jest bardzo podobne do powielenia pliku, ponieważ oryginalny plik pozostaje bez zmian na swoim miejscu. Przekazywanie przez referencję przypomina tworzenie skrótu do oryginalnej wartości. Gdy utworzysz skrót do pliku tekstowego, otworzysz skrót i edytujesz plik, wprowadzone przez Ciebie zmiany zostaną zapisane w oryginalnym pliku, do którego się podłączyłeś. Rysunek 4.1. Kopiowanie pliku jest podobne do przekazywania parametru przez wartość, podczas gdy tworzenie skrótu przypomina przekazanie parametru przez referencję
Poniżej przedstawiony został przykład przekazywania parametru przez wartość. Zauważ, że oryginalna wartość nie ulega zmianie: function limitPercentage(percentage:Number):Number { // upewnij się, czy wartość procentowa jest mniejsza od 100 percentage = Math.min(percentage, 100); // upewnij się, czy wartość procentowa jest większa od 0 percentage = Math.max(percentage, 0); return percentage; } var effort:Number = 110; var trimmedPercentage:Number = limitPercentage(effort); trace(effort, "%"); // wyświetli: 110% trace(trimmedPercentage, "%"); // wyświetli: 100%
144
Część I Rozpoczęcie pracy z ActionScript 3.0
Początkowa wartość zmiennej effort nie zmieniła się, chociaż zmienna argumentu percentage zmieniła wartość podczas wykonywania funkcji. Dla typów złożonych sprawa wygląda inaczej. Są przekazywane przez referencję, co oznacza, że argument zachowuje się jak skrót lub odsyłacz do oryginalnego pliku. Zmiany argumentu są odzwierciedlane jako zmiany bezpośrednio na przekazywanej wartości. Większość typów danych przekazuje referencje podczas przypisywania zmiennej wartości. Poniższy kod przedstawia, jak zmienna employee jest bezpośrednio łączona z obiektem przekazywanym jako parametr. function capitalizeEmployeeName(employee:Object):void { if (employee.name != null) { employee.name = employee.name.toUpperCase(); } } var person:Object = {name: "Jan Kowalski"}; capitalizeEmployeeName(person); trace(person.name); // wyświetli: JAN KOWALSKI
Jak widać w tym przykładzie, zmiana wielkości liter jest przeprowadzana bezpośrednio na zmiennej employee. Dzieje się tak dlatego, że wartość employee odwołuje się bezpośrednio do oryginalnej kopii person — dlatego nazwisko osoby jest również powiązane. Poniżej wypisane typy danych przekazywane są przez wartość: String Number int uint Boolean Null void
Pozostałe typy danych są przekazywane przez referencję.
Ustawianie wartości domyślnych Nowością w ActionScript 3.0 jest możliwość ustawiania domyślnych wartości dla argumentów metody. Aby tego dokonać, dodaj za nazwą argumentu znak równości (=) oraz domyślną wartość: function showGreeting(name:String = "obcy"):void { trace("Witaj, " + name + ", miło Cię poznać."); } showGreeting("Andrzej); // wyświetli: Witaj, Andrzej, miło Cię poznać. showGreeting(); // wyświetli: Witaj, obcy, miło Cię poznać.
Jak widzisz na przykładzie drugiego wywołania, imię zostało zastąpione domyślną wartością "obcy". Podczas stosowania domyślnych wartości należy pamiętać o jeszcze jednej zasadzie. Wszystkie argumenty posiadające domyślną wartość są uznawane za opcjonalne. Jako takie muszą być umieszczone na końcu listy argumentów. Dlatego też następujący kod: function storeAddress(name:String, zip:String = null, email:String)
Rozdział 4. Praca z metodami i funkcjami
145
nie jest poprawnym kodem, ponieważ email jest parametrem wymaganym, a występuje za zip, który jest opcjonalny. Poprawna kolejność wygląda w tym przypadku następująco: function storeAddress(name:String, email:String, zip:String = null)
Użycie argumentu reszty (...) ActionScript 3.0 wprowadza nowe rozwiązanie nazywane parametrem reszty (...). Parametr reszty oznaczany jest symbolem ..., reprezentującym zmienną liczbę argumentów w postaci tablicy. Po nim następuje nazwa tej tablicy. Pisany jest jako trzy kropki, po których następuje nazwa używana dla tablicy zawierającej te wartości. Dodanie do własnej funkcji parametru reszty umożliwi Ci wywoływanie funkcji z dowolną liczbą parametrów. Jeśli np. potrzebujesz funkcji dodającej do siebie dowolną ilość liczb, możesz użyć parametru reszty: function sum(... numbers):Number { var result:Number = 0; for each (var num:Number in numbers) { result += num; } return result; } trace(sum(1,2,3,4,5)); // wyświetli: 15
Wartości przekazane do funkcji zawarte są w tablicy reszty o nazwie numbers. Dostęp do elementów tej tablicy uzyskasz, przechodząc w pętli po jej kolejnych wartościach. Tablice zostały dokładniej omówione w rozdziale 8.
Parametr reszty może zostać również użyty z innymi, wymaganymi parametrami. Wymagane parametry jak zwykle będą posiadać swoje własne nazwy i będą istnieć niezależnie od tablicy reszty. Wszystkie dodatkowe parametry po tych wymaganych zapisane zostaną w tablicy reszty. Zmodyfikujmy poprzedni przykład tak, aby wymagał przekazania przynajmniej jednego argumentu: function sum(base:Number, ... numbers):Number { var result:Number = base; for each (var num:Number in numbers) { result += num; } return result; } trace(sum()); // zgłosi błąd czasu wykonywania trace(sum(1,2,3,4,5)); // wyświetli: 15
Dostęp do obiektu arguments Klasa Function definiuje pojedynczą właściwość, która jest wykorzystywana do przechowywania informacji na temat danych przekazywanych do funkcji podczas jej wywoływania. Właściwość ta nazywa się arguments. Jest to obiekt specjalnego typu danych Arguments, który pomimo iż zachowuje się podobnie do tablicy, technicznie nie jest tablicą. Każda funkcja jest instancją klasy Function, przez co posiada właściwość arguments.
146
Część I Rozpoczęcie pracy z ActionScript 3.0 Właściwość arguments w ActionScript 3.0 straciła na znaczeniu i pozostawiono ją jedynie w celu obsługi starszego kodu. Zalecamy zamiast niej używanie nowego parametru reszty (...). Parametr reszty i obiekt arguments nie mogą być używane razem. Dołączenie argumentu reszty w Twojej funkcji wyłączy obiekt arguments.
Użyteczność obiektu arguments jest blokowana w trybie standardów, ponieważ tryb ten wymaga dostarczenia dokładnej liczby argumentów zdefiniowanych w deklaracji funkcji. Jeśli zdecydujesz się na użycie obiektu arguments, możesz wyłączyć kompilowanie w trybie standardów.
Uzyskiwanie wartości z właściwości arguments Właściwość arguments jest bardzo podobna do parametru reszty w tym, że zawiera każdy parametr, który jest przekazywany do funkcji w postaci uporządkowanej tablicy. Główna różnica pomiędzy nimi polega na tym, że w tablicy argumentów znajdą się wszystkie nazwane, jak również nienazwane argumenty, podczas gdy w parametrze reszty zawarte są tylko nienazwane argumenty. Do parametrów przekazywanych do funkcji można uzyskać dostęp za pomocą składni tablicowej. Pierwszy przekazany parametr będzie posiadać indeks 0, drugi indeks 1 i tak dalej. Poniższy przykład zostanie skompilowany poprawnie, gdy tryb standardów będzie wyłączony: function sum():Number { var result:Number = 0; for each (var num:Number in arguments) { result += num; } return result; } trace(sum(1,2,3,4,5)); // wyświetli: 15
Właściwość arguments.caller nie jest już dostępna w ActionScript 3.0.
Zwracanie wyników Funkcje potrafią o wiele więcej niż zwykłe wykonywanie predefiniowanego kodu — to zaledwie połowa ich zastosowania. Funkcje w ActionScript potrafią podobnie jak funkcje matematyczne obliczać wartości w oparciu o wprowadzone zmienne. Możliwość ta jest bardzo pożyteczna. Możesz traktować funkcję tak, jakby była fabryką przekształcającą surowce w gotowe produkty, co przedstawiono na rysunku 4.2. W tym przypadku surowce są argumentami przekazywanymi do wywołania funkcji, a końcowy produkt jest wartością zwracaną przez funkcję.
Rozdział 4. Praca z metodami i funkcjami
147
Rysunek 4.2. Funkcje, podobnie jak fabryki, przekształcają surowce w gotowy produkt
Zwracanie wartości za pomocą wyrażenia return W najprostszej formie instrukcja zwracająca jest słowem kluczowym return, po którym następuje zwracana wartość. Wartość może być typu prostego, może być zmienną, wynikiem działania innej funkcji czy dowolnym innym typem wyrażenia. return someValue;
Zauważ, że w instrukcji return nie używa się nawiasów. Wynika to z faktu, że nie jest to funkcja, ale raczej operator. Możesz natknąć się czasem na nawiasy — np. return (someValue) — ale nie są one wymagane. W większości sytuacji będziesz chciał w określony sposób manipulować argumentami wewnątrz funkcji, a następnie zwrócić nową, obliczoną wartość. Poniższy przykład oblicza obwód okręgu poprzez pobranie średnicy jako parametru diameter, pomnożenie jej przez matematyczną stałą pi i zwrócenie wyniku w postaci liczby: function getCircumference(diameter:Number):Number { var circumference:Number = diameter * Math.PI; return circumference; } trace(getCircumference(25)); // wyświetli: 78.53981633974483
Jak widzisz, wywołanie metody getCircumference() może zostać użyte w instrukcji trace, tak jakby było zmienną. Wyniki działania funkcji definiującej typ zwracany mogą być również zapisywane w zmiennej. Realizuje się to poprzez umieszczenie zmiennej z lewej strony znaku równości. Gdy tylko w funkcji wykonana zostanie instrukcja return, funkcja kończy się, a reszta jej kodu nie jest przetwarzana. Z tego też powodu każda funkcja może zwrócić tylko jedną wartość. Instrukcja return może też zostać użyta do przedwczesnego zakończenia funkcji. function getColor():String { var color:String = "czerwony"; return color; color = "niebieski"; return color; } var color:String = getColor(); trace(color); // wyświetli: czerwony
148
Część I Rozpoczęcie pracy z ActionScript 3.0
Ta metoda zawsze będzie zwracać „czerwony”, ponieważ wykonywanie kodu tej funkcji zostaje zatrzymane po pierwszej instrukcji zwracającej. Chcąc zwrócić z funkcji więcej niż jedną wartość, możesz utworzyć obiekt lub jeszcze lepiej własną klasę, która będzie przechowywać wszystkie Twoje odpowiedzi w jednej zmiennej. return {firstName:"Paul", lastName:"Newman"};
Definiowanie typu zwracanej wartości dla Twojej funkcji Gdy definiujesz typ danych dla zmiennej, stałej czy argumentu za pomocą dwukropka (:) po nazwie obiektu, wskazuje to kompilatorowi, jaki rodzaj informacji powinien być przechowywany w tym obiekcie. Funkcje działają w ten sam sposób. Wszystkie funkcje definiujące zwracany typ muszą honorować go poprzez zwracanie wartości za pomocą instrukcji return, odpowiadającej typowi danych. I odwrotnie: wszystkie funkcje używające instrukcji return powinny posiadać typ zwracany. Zwracany typ — podobnie jak typ dołączony do każdego argumentu — wskazuje kompilatorowi, jakiego rodzaju danych ma się spodziewać, gdy funkcja zwróci wartość. Konstruktory są przypadkiem specjalnym. Może Cię kusić użycie nazwy klasy jako zwracanego typu dla Twojego konstruktora, jednak ActionScript nakazuje pominięcie typu zwracanego dla wszystkich konstruktorów — np. public function MyClass() {...}. Więcej informacji na temat konstruktorów znajdziesz w rozdziale 3. W AS3 ścisłe typowanie zmiennych i funkcji nie jest wprawdzie wymagane, ale wysoce zalecane. Zauważysz, że Twój kod będzie wykonywał się szybciej przy zastosowaniu sprawdzania typów w czasie wykonywania — nie wspominając już o tym, że będzie prostszy w czytaniu, debugowaniu, użyciu i ponownym zastosowaniu.
Jeśli nie jesteś pewien, jakiego typu dane będziesz zwracać, możesz użyć typu danych reprezentowanego przez wieloznacznik (*), który odpowiada danym bez przypisanego typu. Umożliwi to zwracanie dowolnego typu danych. W przedstawionym poniżej przykładzie typ utworzonych danych jest przekazywany do wywołania funkcji. Jak widzisz, funkcja ta posiada wiele zwracanych typów, ale tylko pierwszy pasujący zostanie wykonany. Reszta nigdy nie zostanie uaktywniona. function createObject(type:String):* { switch (type) { case "Boolean": return new Boolean(); case "Number": return new Number(); case "String": return new String(); case "Array": return new Array(); default: trace("Nieznany typ"); return null; } } var myObject:* = createObject("Array"); myObject.push(1, 2, 3, 4, 5); trace(myObject); // wyświetli: 1, 2, 3, 4, 5
Rozdział 4. Praca z metodami i funkcjami
149
Typ reprezentowany przez wieloznacznik może być w pewnych okolicznościach bardzo pożyteczny, jednak nie powinieneś go nadużywać. Większość Twoich metod powinna posiadać określony typ danych.
Zwracanie void Jeśli nie potrzebujesz, żeby Twoja funkcja coś zwracała, użyj typu zwracanego void. Dla funkcji zwracających void nie jest wymagana instrukcja return. function doNothing():void { // Nie będę robić niczego. }
Nawet jeśli ten przykład „nie robi niczego”, funkcje niezwracające wartości są bardzo powszechnie stosowane. To, że nie zwracają one wartości, wcale nie oznacza, że nie wykonują pożytecznego kodu. W rzeczywistości duża część funkcji w Twoim kodzie prawdopodobnie nie będzie posiadać instrukcji zwracających. Czasami funkcje niezwracające wartości nazywa się procedurami — małymi, nadającymi się do wielokrotnego stosowania blokami kodu, które mają na celu zamknięcie w sobie często wykonywanych zadań. Od ActionScript 3.0 typ danych Void jest pisany małą literą, co służy większej jednolitości z innymi językami programowania. Jeśli przyzwyczaiłeś się do pisania kodu w AS2, uważaj, aby nie popełnić błędu, i nie miej złego samopoczucia, jeśli początkowo kilka takich pomyłek Ci się przytrafi.
Definiowanie funkcji za pomocą wyrażeń funkcyjnych Do tej pory przyglądaliśmy się definiowaniu funkcji za pomocą instrukcji function. Istnieje jednak alternatywny sposób definiowania funkcji. ActionScript obsługuje również wyrażenia funkcyjne. Posługując się tą techniką, najpierw definiujesz funkcję jako zmienną o typie danych Function, a następnie ustawiasz wartość tej zmiennej na wyrażenie funkcyjne. var doSomething:Function = function(arg:Object):void { // kod funkcji umieszczamy tutaj. }
Wyrażenia funkcyjne zachowują się trochę odmiennie od instrukcji funkcyjnych. W tabeli 4.1 zamieszczono kilka kluczowych różnic. Dla większości zastosowań zalecamy używanie w kodzie instrukcji funkcyjnych zamiast wyrażeń funkcyjnych. Spójność i prostota ich stosowania czynią je pierwszorzędnym wyborem. Istnieją jednak pewne sytuacje, w których zastosowanie wyrażenia funkcyjnego da Ci elastyczność i pożyteczność, jakiej instrukcja funkcyjna nie potrafi dać. Można do nich zaliczyć:
Jednorazowe użycie danej funkcji.
Zdefiniowanie funkcji wewnątrz innych funkcji.
150
Część I Rozpoczęcie pracy z ActionScript 3.0
Tabela 4.1. Różnice pomiędzy instrukcjami funkcyjnymi a wyrażeniami funkcyjnymi Instrukcja funkcji
Wyrażenie funkcyjne
Może być wywoływane z dowolnego miejsca Podobnie jak deklaracje zmiennej dostępne jest tylko w kodzie, niezależnie od tego, gdzie w kodzie po wystąpieniu kodu, w którym funkcja została zdefiniowana. zdefiniowana jest funkcja. Łatwiejsza do czytania.
Może być trudna i kłopotliwa w czytaniu.
Może być wywoływana jako metoda za pomocą składni kropkowej.
W trybie standardów nie może być wywoływana jako metoda. Zamiast tego używa się składni kwadratowych nawiasów.
Istnieje w pamięci jako część obiektu, w którym jest zawarta.
Istnieje w pamięci niezależnie. Dobre dla funkcji „do wyrzucenia”, które nie muszą być zachowywane w pamięci na długo.
Nie może zostać nadpisana lub usunięta.
Można mu przypisać nową wartość lub usunąć je za pomocą operatora delete.
Dołączenie metody do prototypu klasy.
Zmianę funkcjonalności metody w czasie wykonywania.
Przekazywanie funkcji jako argumentu dla innej funkcji.
Dostęp do metod superklasy Zgodnie z tym, czego dowiedziałeś się z rozdziału 3., klasy ActionScript posiadają zdolność rozszerzania funkcjonalności innej klasy. Klasa rozszerzająca funkcjonalność nazywana jest podklasą, a klasa rozszerzana nazywana jest superklasą. Koncepcja ta zwana jest dziedziczeniem. Podczas tworzenia klasy masz do czynienia ze specjalnym mechanizmem, który może zostać wykorzystany do uzyskiwania dostępu do metod superklasy. Jest nim słowo kluczowe super. Słowo kluczowe super pozwala na wywołanie lub rozszerzenie istniejących metod zdefiniowanych w superklasie. Gdy używane jest samodzielnie, odwołuje się do funkcji konstruktora superklasy. Oznacza to, że może zostać użyte do wywołania całego kodu w funkcji konstruktora superklasy lub klasy rodzica. Jest to najbardziej powszechne zastosowanie. W poniższym przykładzie kod powinien być zapisany w pliku MySprite.as: package { import flash.display.*; public class MySprite extends Sprite { public function MySprite() { super(); x = 100; y = 100; } } }
Rozdział 4. Praca z metodami i funkcjami
151
W tym przykładzie super() zostało użyte do wywołania całego kodu funkcji konstruktora klasy Sprite, po czym następuje ustawienie x i y dla MySprite na wartość 100. Obiekt super może zostać także użyty do uzyskania dostępu do pozostałych metod superklasy. Użyj po prostu składni super.method() do wywołania metody superklasy. W poniższym kodzie przesłaniamy metodę addChild() klasy Sprite, aby wyświetlić obiekt potomka, który dodajemy: package { import flash.display.*; public class MySprite extends Sprite{ override public function addChild(child:DisplayObject):DisplayObject { var addedChild:DisplayObject = super.addChild(child); trace("Właśnie dodany potomek " + addedChild.toString()); return addedChild; } } }
Poprzez wywołanie super.addChild(child) przekazujemy parametr child do metody addChild() superklasy. W rezultacie wykonany zostaje oryginalny kod addChild(), a jego wynik jest zapisywany do zmiennej addedChild. Następnie wyświetlana jest wartość tekstowa addedChild. Na końcu jest ona zwracana.
Przesłanianie metod Być może zwróciłeś uwagę w poprzednim przykładzie na to, iż sygnatura funkcji dla metody addChild() została poprzedzona słowem kluczowym override. Powoduje to, że definiowana funkcja przesłania lub ponownie definiuje metodę superklasy. Nowa metoda musi posiadać dokładnie taką samą sygnaturę (listę parametrów funkcji, zwracany typ itd.), co metoda przesłaniana. Jeśli nie masz pewności co do dokładnej sygnatury metody, zajrzyj do dokumentacji w celu sprawdzenia budowy tej metody. Słowo kluczowe override musi być umieszczone przed wszystkimi przesłanianymi metodami. Pominięcie go spowodowałoby błąd podczas kompilowania. Metody zdefiniowane za pomocą słowa kluczowego private (a w pewnych przypadkach internal) nie wymagają słowa kluczowego override, ponieważ nie są przekazywane w dół do podklas.
Pisanie funkcji rekurencyjnych Czasami funkcja w celu zwrócenia wartości musi być wywoływana wielokrotnie, a jej dane wejściowe są zależne od wyniku jej poprzednich wywołań. Taka sprytna forma projektowania funkcji znana jest jako rekurencja. Funkcje rekurencyjne są funkcjami wywołującymi siebie wewnątrz swojego ciała funkcji w celu utworzenia wyniku bazującego na wielu poziomach rekurencji. Jest to zaawansowana technika i początkującym programistom może pierwotnie wydawać się dziwna, dlatego jeśli chcesz, możesz pominąć to zagadnienie i powrócić do niego, jeśli kiedykolwiek będziesz musiał dowiedzieć się czegoś więcej na ten temat.
152
Część I Rozpoczęcie pracy z ActionScript 3.0
Oto kilka zastosowań, w których mógłbyś użyć funkcji rekurencyjnej:
Podczas przemieszczania się po zagnieżdżonych danych, jak w przypadku pliku XML czy listy właściwości.
Podczas obliczania funkcji matematycznych, które bazują na wielu iteracjach, jak zagadnienie silni czy obliczenia statystyczne.
Funkcje wymagające wracania do poprzednich iteracji lub przechodzące przez złożony zestaw możliwych rozwiązań, jak algorytm poruszania się po labiryncie.
Przyjrzyjmy się często wykorzystywanej funkcji rekurencyjnej — silni. W matematyce funkcja silnia, zazwyczaj zapisywana jako dodatnia liczba całkowita, po której występuje znak wykrzyknika (4!), jest funkcją obliczającą iloczyn wszystkich dodatnich liczb całkowitych mniejszych bądź równych danej liczbie całkowitej. Innymi słowy, 3! = 3·2·1 = 6. Możemy ująć to w postaci funkcji rekurencyjnej: function factorial(i:uint) { if (i == 0) { return 1; } else { return (i * factorial(i - 1)); } } trace(factorial(3)); // wyświetli : 6
Co się dzieje, gdy wywołujesz tę funkcję? Przekazywana jest początkowa wartość 3. Wartość 3 jest większa od 0, dlatego pomijamy pierwszą instrukcję warunkową i przechodzimy do instrukcji else. Zwraca ona wartość i pomnożoną przez silnię i-1 (2). Jednak przed zwróceniem wartości instrukcja silni uruchamiana jest ponownie drugi raz z wykorzystaniem 2 jako parametru. Cały proces rozpoczyna się od nowa. factorial(2) zwraca i * factorial(i-1) (lub 2 razy silnia z 1). Całość powtarzana jest tak długo, aż przetworzone zostaną wszystkie liczby całkowite i zakończy się pętla. Ostatecznie wszystkie zwrócone wartości zostaną wysłane w celu pomnożenia ich przez funkcję.
Leczenie z rekursivitis Jak mogłeś się przekonać we wcześniejszym przykładzie, praca z funkcjami rekurencyjnymi może szybko się skomplikować, co znacznie utrudni wykrywanie błędów. Nazywam to objawami rekursivitis (nie, to nie jest prawdziwy termin medyczny). Jednym z rozwiązań tego problemu, a uważam je za przydatne, jest zapisywanie wartości zmiennych na zwykłej kartce papieru i analizowanie tego, co się wydarzy podczas każdej z iteracji. Dla wcześniej przedstawionego przykładu mógłbym utworzyć graf pokazany na rysunku 4.3. Rysunek 4.3. Leczenie z rekursivitis starym, sprawdzonym sposobem
Rozdział 4. Praca z metodami i funkcjami
153
Jak widzisz, dla każdej iteracji śledzę zmiany poprzez ich zapisywanie. Analizowanie kodu w ten sposób jest o wiele prostsze. Z czasem, gdy Twoje zagadnienia będą coraz bardziej złożone, możesz skorzystać z wbudowanego debugera. Narzędzia debugujące opisujemy w rozdziale 20. Pracując z funkcjami rekurencyjnymi, na pewno przynajmniej raz spotkasz się z problemem przepełnienia stosu. Występuje on wtedy, gdy funkcja nie posiada mechanizmu kończącego jej działanie. Problem podobny do tego to pętle nieskończone, które mogą wystąpić podczas pracy z pętlami for oraz while. Dokładniej opisaliśmy go w rozdziale 2. Flash Player posiada wbudowany mechanizm zatrzymujący każdą pętlę przed wykonaniem zbyt wiele razy. Jeśli Twoja funkcja rekurencyjna powtórzy się zbyt wiele razy, możesz uzyskać taką informację o błędzie: Error: Error #1502: A script has executed for longer than the default timeout period of 15 seconds.
Funkcje jako obiekty Warto powtórzyć jeszcze raz: prawie wszystko w ActionScript jest obiektem. Funkcje nie są tutaj żadnym wyjątkiem. Każda funkcja w ActionScript 3.0 jest instancją klasy Function z właściwościami i metodami — jak każdy inny obiekt. W tym podrozdziale pokażemy, jak używać tej relacji z korzyścią dla siebie.
Function a function Słowo function pisane małą literą jest ogólnym określeniem, którego używaliśmy do tej pory w tym rozdziale do opisywania wykonywalnego bloku kodu. Function pisane dużą literą oznacza klasę Function, której instancjami są wszystkie funkcje. Na ogół, tworząc nową instancję klasy, używa się słowa kluczowego new, po którym następuje nazwa klasy. Ponieważ funkcje są integralnym i często używanym elementem ActionScript, występują polecenia (jak słowo kluczowe function), które używane są zamiast instrukcji new Function(). Technicznie zapis new Function()jest poprawny, ale nie robi zbyt wiele poza utworzeniem nowej, niezdefiniowanej funkcji.
Najbardziej prawdopodobne jest, że z nazwą klasy Function spotkasz się podczas pracy z metodami wymagającymi funkcji na wejściu lub podczas tworzenia funkcji za pomocą wyrażenia. Np. metoda addEventListener() pobiera jako parametr funkcję. Jeśli nie wiesz jeszcze, co to są zdarzenia, nie przejmuj się. Opiszemy je bardzo szczegółowo w części IV tej książki. Teraz przyjrzyjmy się, jakich argumentów funkcja oczekuje. addEventListener(type:String, listener:Function):void
addEventListener() akceptuje pięć argumentów. W celu zachowania prostoty dołączono tutaj jedynie dwa wymagane.
154
Część I Rozpoczęcie pracy z ActionScript 3.0
Jak widzisz, drugi oczekiwany przez addEventListener() parametr jest funkcją nasłuchującą. Jest to funkcja, która zostanie wykonana, gdy wystąpi zdarzenie wskazanego typu (type). Jeżeli chcesz przekazać funkcję jako argument do innej funkcji, użyj nazwy przekazywanej funkcji, ale opuść nawiasy (operator wywołania). function onClick(event:MouseEvent):void { // obsłuż kliknięcie } addEventListener(MouseEvent.CLICK, onClick);
Pamiętaj tutaj o pominięciu nawiasów. Jeśli napiszesz addEventListener(MouseEvent. CLICK, onClick());, wtedy funkcja onclick() zostanie wykonana i zwróci void, co zostanie przekazane jako Twój parametr (void oczywiście nie jest funkcją).
Metody i właściwości klasy Function Klasa Function posiada niewiele właściwości i metod. Jako podklasa klasy Object dziedziczy wszystkie jej metody i właściwości, jak toString(). Oprócz tych dziedziczonych wartości klasa Function definiuje swoje własne metody i właściwości. Już przyglądaliśmy się arguments, jedynej właściwości definiowanej przez Function. Przyjrzyjmy się teraz jej metodom. Klasa Function definiuje dwie metody: call() oraz apply(), będące alternatywnymi sposobami wywoływania funkcji. Jednak funkcje te nie działają tak samo w ActionScript 3.0, jak działały wcześniej w ActionScript 2.0. W większości przypadków thisObject jest kompletnie ignorowany. Zalecamy nie korzystać z tych metod. Jeśli musisz wywołać metody niezdefiniowane w klasie, rozważ użycie klasy flash.util.Proxy. Więcej informacji na ten temat znajdziesz w oficjalnej dokumentacji ActionScript 3.0.
Podsumowanie
Metody oraz funkcje są powtarzalnymi operacjami wykonującymi bloki kodu w oparciu o przekazane parametry wejściowe.
Użycie operatora return pozwoli Ci obliczyć i zwrócić wartości w zależności od zmiennych wprowadzonych do Twojej funkcji.
Metody — podobnie jak większość innych elementów w ActionScript 3.0 — są obiektami. Należą do klasy Function.
Funkcje można tworzyć za pomocą instrukcji function lub poprzez przypisanie wartości do obiektu typu Function za pomocą wyrażeń funkcyjnych.
Parametr ... (reszta) może zostać użyty do uzyskania dostępu do wszystkich argumentów funkcji, jeżeli liczba argumentów jest nieznana lub zmienna.
Słowa kluczowe super oraz override mogą zostać użyte przez podklasę w celu dodania funkcjonalności do metody superklasy.
Funkcje rekurencyjne umożliwiają powtarzanie akcji i łączenie wyników wygenerowanych przez jej poprzednie wywołania.
Rozdział 5.
Walidacja programów W tym rozdziale:
Błędy
Często występujące błędy kompilacji i czasu wykonania
Niezależnie od tego, jak byś się starał podczas pisania kodu, prawie zawsze popełnisz jeden lub dwa błędy. Nie przejmuj się jednak, to zdarza się każdemu. W rzeczywistości nawet my, autorzy tej książki, borykamy się z błędami przy każdym podejściu do pisania kodu. Naprawdę nie ma się czego wstydzić, a jak się niedługo przekonasz, możliwość zauważenia błędów jest prawdziwym błogosławieństwem. Błędy są Twoimi przyjaciółmi... no cóż, przynajmniej niektóre. Twoim celem zawsze powinno być dążenie do wolnego od błędów kodu, jednak komunikaty o błędach są najlepszym sposobem poinformowania Cię o problemach, których sam nie zauważyłeś. Traktuj to jak sprawdzanie pisowni. Systematyczne kompilowanie kodu i wyszukiwanie błędów jest najlepszym sposobem wychwycenia problemów, zanim zaczną się mnożyć. ActionScript 3.0 wprowadza znacznie ulepszony system identyfikacji i obsługi błędów. Debugowanie kodu oraz poprawianie błędów jest teraz o wiele prostsze niż wcześniej. Więcej na temat błędów i zapisu logów napiszemy w dalszej części tej książki, a tutaj chcemy skupić się na najczęściej występujących pluskwach i sposobie ich usuwania.
Wprowadzenie do błędów Komunikaty o błędach są wiadomościami generowanymi przez kompilator lub program Flash Player, informującymi Cię, gdy coś pójdzie nie tak, jak powinno. Nowa funkcja w kompilatorze AS3 pozwala wybrać, czy kompilator ma interpretować kod w trybie standardów, czy nie. Tryb standardów, będący domyślnym ustawieniem, umożliwia kompilowanie kodu z zastosowaniem bardziej rygorystycznego sprawdzania typów podczas kompilacji. Zanim zezwoli programowi na kompilację, sprawdza, czy wszystkie klasy, metody i właściwości są poprawne — jest po prostu bardziej ścisły. Czyni to debugowanie o wiele łatwiejszym. Z drugiej strony, w trybie standardów zabroniony jest dynamiczny dostęp do elementów niedynamicznych obiektów. Tryb odpowiadający wyłączeniu trybu standardów bardzo przypomina kompilator programu Flash 8, który nie przeprowadzał podczas kompilacji sprawdzania typów.
156
Część I Rozpoczęcie pracy z ActionScript 3.0 Wszystkie przykłady zamieszczone w tej książce powinny być uruchamiane z włączonym trybem standardów, chyba że napiszemy inaczej.
Błędy kompilacji a błędy czasu wykonywania Istnieją dwie podstawowe kategorie błędów, z którymi spotkasz się, pisząc kod w ActionScript 3.0: błędy kompilacji i błędy czasu wykonywania.
Błędy kompilacji Błędy kompilacji występują podczas próby skompilowania pliku SWF. Te typy błędów zazwyczaj wskazują na błędną składnię lub odwołanie do nieistniejącej klasy. Chcąc poprawnie skompilować swój program, musisz wyeliminować te błędy.
Błędy czasu wykonywania Błąd czasu wykonywania występuje podczas wykonywania pliku SWF, gdy program jest uruchomiony. Zazwyczaj jest skutkiem problemu, który pojawił się już po rozpoczęciu Twojego programu, jak np. odwołanie do usuniętego wcześniej obiektu. Błędy czasu wykonywania mogą prowadzić w programie do nieoczekiwanych rezultatów. Niektóre mogą spowodować zakończenie przez program działania, podczas gdy inne mogą pozostać niezauważone. W każdym z tych przypadków należy naprawić wszystkie wystąpienia błędów w programie. Chcąc mieć możliwość oglądania błędów czasu wykonywania w swojej przeglądarce internetowej, musisz zainstalować wersję debugującą programu Flash Player, dostępną pod adresem: www.adobe.com/support/flashplayer/downloads.html.
Ostrzeżenia Czasami kompilator zamiast błędu zgłasza ostrzeżenie. Ostrzeżenia dotyczą problemów nieblokujących kompilacji kodu czy jego prawidłowego funkcjonowania, ale i te problemy powinno się wyeliminować w celu posiadania czystego, czytelnego i spójnego kodu. Powszechnym przykładem generującym ostrzeżenie jest zapomnienie o dodaniu typu danych do zmiennej lub typu zwracanego z funkcji. Dobrze jest traktować ostrzeżenia tak, jakby były błędami.
Informacje zwrotne od programów Flash CS3 oraz Flex Builder Flash CS3 oraz Flex Builder różnią się trochę podejściem do identyfikacji błędów. Poniższe informacje powinny umożliwić Ci rozpoczęcie pracy niezależnie od używanego oprogramowania.
Rozdział 5. Walidacja programów
157
Zarówno Flash CS3, jak i Flex Builder posiadają tryby debugowania oferujące zaawansowane narzędzia do obsługi błędów. Nie opisujemy ich w tym rozdziale, za to informacje na ich temat znajdziesz w rozdziale 20.
Debugowanie w programie Flash CS3 W programie Flash CS3 okno Wyjście (dla instrukcji trace oraz błędów czasu wykonania) jest wyświetlane automatycznie podczas testowania filmu. Jeśli wystąpią błędy kompilacji, wyświetlone zostanie też okno Błędy kompilacji. Użyjmy poniższego fragmentu kodu do przetestowania okna Błędy kompilacji. Występuje tu ewidentny problem z wywołaniem trace(): trase("jestem dbry w pisaniu");
Podczas kompilacji wyświetlone zostanie okno Błędy kompilacji ze zgłoszonym jednym błędem, co pokazano na rysunku 5.1. Okno wskazuje na lokalizację błędu w pliku AS lub na scenie, warstwie czy klatce pliku FLA razem z numerem wiersza. Jeżeli zaznaczysz błąd i klikniesz przycisk Przejdź do źródła w dolnym prawym narożniku okna, kursor zostanie przeniesiony właśnie do tego miejsca w kodzie. W oknie tym znajduje się również kolumna Opis, w której powinny się znaleźć numer błędu oraz jego opis. Kolumna Źródło przedstawia kod zamieszczony w danym wierszu.
Rysunek 5.1. Okno Błędy kompilacji w programie Flash CS3 pokazuje, że nie ma metody o nazwie „trase”
Oprócz okna Błędy kompilacji dostępne jest też okno Wyjście. Pokazuje ono wyniki zwracane przez funkcję trace() oraz logi, jak również błędy zgłaszane podczas wykonywania kodu. Uruchomienie kodu: var foo:Array = null; foo.length;
da wynik przedstawiony na rysunku 5.2. Rysunek przedstawia stos wywołań wykonywany w momencie wystąpienia błędu. Kod wykonywany z osi czasu pokaże numer klatki jako funkcję w stosie wywołań. Rysunek 5.2. Okno Wyjście w programie Flash CS3
158
Część I Rozpoczęcie pracy z ActionScript 3.0
Debugowanie w programie Flex Builder Jedną z głównych zalet programu Flex Builder jest możliwość informowania Cię o problemach w kodzie jeszcze podczas pisania kodu. Gdy pisze się kod źródłowy, obok wierszy zawierających błędy kompilacji pojawią się znaczniki (z lewej strony numerów wierszy). Ustawienie kursora myszy nad tymi znacznikami lub otwarcie widoku Problems (problemy) umożliwi dostęp do szczegółowej informacji na temat danego problemu, co pokazano na rysunku 5.3. Aby przejść do wiersza kodu, w którym wystąpił błąd, możesz kliknąć czerwone znaczniki odwołania obok paska przewijania z prawej strony kodu lub dwukrotnie kliknąć element w widoku Problems. Rysunek 5.3. Program Flex Builder pokazujący znacznik niepoprawnego wiersza oraz jego odpowiednika — widok Problems
Ostrzeżenia zgłaszane są w podobny sposób, ale za pomocą koloru żółtego zamiast czerwonego. Gdy Flex Builder jest ustawiony tak, aby automatycznie budował program, będziesz oglądać błędy przy każdym zapisywaniu. Możesz włączać i wyłączać tę funkcję poprzez wybór z menu Project/Build Automatically (projekt/buduj automatycznie).
Podobnie jak program Flash CS3 posiada okno Wyjście, tak Flex Builder posiada widok Console, pokazany na rysunku 5.4, a służący do wyświetlania wyników zwracanych przez funkcję trace(), logów oraz komunikatów o błędach podczas debugowania.
Rozdział 5. Walidacja programów
159
Rysunek 5.4. Flex Builder pokazujący błąd czasu wykonywania w widoku Console (konsola)
Naprawianie błędów Gdy w danej sekcji kodu zostanie zgłoszony błąd kompilacji, uzyskasz dokładną informację o nazwie pliku oraz numerze wiersza tego wystąpienia. Jest to doskonały punkt wyjścia podczas próby rozwiązania problemów w tworzonym programie. Przejdź do wiersza, w którym odnaleziony został błąd, i szukaj literówek lub pomyłek mogących być odpowiedzialnymi za wywołanie tego błędu. Błędy czasu wykonania wyświetlają bieżący stos wywołań, będący listą wszystkich funkcji, które były wykonywane w momencie wystąpienia błędu. Analiza stosu wywołań pozwoli Ci na prześledzenie drogi wstecz do początkowej lub początkowych funkcji odpowiedzialnych za zaistniały problem. Rozważ poniższy fragment kodu, powodujący zgłoszenie błędu typu, ponieważ zmienna nullSprite nie posiada wartości: package pl.helion.as3biblia { import flash.display.Sprite; public class BuggySprite extends Sprite { public function BuggySprite () { var nullSprite:Sprite = null; updateLayout(nullSprite); } public function updateLayout(sprite:Sprite):void { moveSprite(sprite); } public function moveSprite(sprite:Sprite):void { sprite.x = 100; } } }
160
Część I Rozpoczęcie pracy z ActionScript 3.0
Ten kod zgłosi następujący błąd: TypeError: Error #1009: Cannot access a property or method of a null object reference/ at BuggySprite/moveSprite() at BuggySprite/updateLayout() at BuggySprite$iinit()
Funkcja $iinit() odwołuje się do konstruktora, w tym przypadku BuggySprite().
Jak widzisz, obie funkcje — BuggySprite() oraz updateLayout() — wywołują wewnątrz swoich bloków funkcji inne funkcje. Z tego też powodu pojawiają się w stosie wywołania. Może to skutecznie przyczynić się do odnalezienia początkowego źródła problemu. (W tym przypadku problemem było przypisanie null w konstruktorze). Możesz zobaczyć wiele błędów kompilacji jednocześnie. Czasami jeden błąd może być przyczyną wystąpienia następnego. Nie przeskakuj pomiędzy nimi podczas naprawiania błędów. Ogólnie rzecz biorąc, najlepiej rozpocząć od pierwszego błędu na liście, naprawić go, zapisać swoje pliki i ponownie skompilować program. W wielu przypadkach rozwiąże to więcej niż tylko jeden problem.
Powszechne typy błędów W tabeli 5.1 zestawiono listę najbardziej popularnych typów błędów, z którymi prawdopodobnie będziesz mieć do czynienia — zwłaszcza jeżeli dopiero rozpoczynasz swoją przygodę z programowaniem w ActionScript 3.0. Tabela 5.1. Lista często zgłaszanych błędów oraz sposoby ich usunięcia Opis błędu
Możliwe przyczyny
Możliwe rozwiązanie
TypeError: Error #1009: Cannot access a property or method of a null object reference.
Powodem jest próba uzyskania dostępu do właściwości lub metod obiektu, któremu nigdy nie była przypisana wartość lub przypisana została wartość null. Jest to nowość dla programistów ActionScript, ponieważ wcześniejsze wersje tego języka nie traktowały tego jako błędu.
Upewnij się, czy poprawnie definiujesz wszystkie obiekty, do których metod lub właściwości chcesz uzyskać dostęp. Możesz też dodać do swojego kodu sprawdzenie obiektu, zanim wywołasz jego metodę. Np.: if(myArray != null) { myArray.pop(); }
C1100: Assignment within To ostrzeżenie pojawia się, Jest to jedna z najczęstszych literówek, conditional. Did you mean gdy przypisanie (np. a=b) znajdzie się a popełnia ją wielu programistów w instrukcji warunkowej, takiej jak if. wszelakich języków programowania. == instead of =? Ponieważ operator przypisania (=) oraz równości (==) są bardzo podobne, często używane są omylnie. Upewnij się, czy wykorzystujesz odpowiedni operator dla swojego zadania. a = b oznacza „Przypisz a wartość b”. a == b oznacza „Czy a jest równe b?”.
Rozdział 5. Walidacja programów
161
Tabela 5.1. Lista często zgłaszanych błędów oraz sposoby ich usunięcia — ciąg dalszy Opis błędu
Możliwe przyczyny
T1180: Call to a possibly Te błędy mogą wystąpić podczas undefined method _____. próby wywołania funkcji, pobrania lub przypisania wartości zmiennej lub lub próby uzyskania dostępu 1119: Access of possibly do obiektu, który nie istnieje. Np.: undefined property _____ var myArray:Array = new Array(); through a reference with myArray.washMyCar(); static type _____. lub 1120: Access of undefined property_____.
Możliwe rozwiązanie Częstą przyczyną tych błędów są literówki. Sprawdź kod i upewnij się, czy wszystko zostało zapisane poprawnie. Pamiętaj o tym, że AS3 rozróżnia wielkość liter, stąd foo, Foo oraz FOO to trzy różne obiekty. Zanim zaczniesz używać zmiennych, upewnij się, czy są one poprawnie zadeklarowane za pomocą instrukcji var. W AS2 słowo kluczowe var było opcjonalne, jednak w AS3 jest już wymagane. Ostatecznie sprawdź, czy nie chcesz przypadkiem uzyskać dostępu do elementu klasy, która wcale nie istnieje. Jeśli to konieczne, sprawdź dokumentację dla klasy powodującej problem, żeby upewnić się, czy dana metoda lub właściwość jest poprawna.
1046: Type was not found Klasa lub interfejs, których chcesz or was not a compile-time użyć, nie zostały odnalezione. Możliwą constant: ____. przyczyną jest błędne odwołanie lub klasa czy interfejs mogą wcale nie istnieć.
Ten błąd podobny jest do trzech wspomnianych wcześniej. Sprawdź kod na ewentualność wystąpienia literówek w nazwach klas. Upewnij się również, czy dołączasz instrukcje import dla każdej klasy nieimportowanej automatycznie. W AS3 nawet niektóre z wbudowanych klas, jak chociażby flash.display.Sprite, muszą być importowane. Ostatecznie sprawdź, czy nazwa pliku i nazwa klasy są takie same.
1067: Implicit coercion of Nastąpiła próba wywołania metody a value of type _____ to ze złą liczbą argumentów (parametrów) an unrelated type _____. lub ze złym typem danych dla jednego z argumentów. lub 1136: Incorrect number of arguments. Expected ___.
Jeśli funkcja definiuje wymagane argumenty, argumenty przekazywane do funkcji podczas jej wywołania muszą odpowiadać sygnaturze tej funkcji. Oznacza to, że musisz dostarczyć odpowiednią liczbę argumentów, a każdy z nich musi być typu, którego ta funkcja się spodziewa. Reguła ta jest bardziej rygorystyczna niż w poprzednich wersjach ActionScript. Jeśli nie jesteś pewien, jakich argumentów użyć, sprawdź dokumentację wywoływanej funkcji.
162
Część I Rozpoczęcie pracy z ActionScript 3.0
Podsumowanie
Błędy przytrafiają się zawsze i to nawet doświadczonym programistom. Błędy zgłaszane przez program Flash Player oraz kompilator są Twoim najlepszym narzędziem do ulepszania kodu.
Usuwaj zgłoszone błędy, rozpoczynając od pierwszego na liście, i przesuwaj się dalej ku końcowi listy. Zazwyczaj naprawienie pierwszego błędu ułatwia naprawienie kolejnych.
Wykorzystuj numery wierszy oraz stos wywołań, które dostarczane są razem z informacją o błędach. Pomogą Ci one w identyfikacji obszaru kodu, w którym dany błąd wystąpił.
Kiedy już nabierzesz doświadczenia, będziesz znać często występujące błędy i będziesz w stanie wyłapać je jeszcze przed ich wystąpieniem.
Więcej informacji na temat błędów i debugowania znajdziesz w części V.
Część II
Praca z obiektami ActionScript 3.0 W tej części: Rozdział 6. Używanie łańcuchów znaków Rozdział 7. Praca z liczbami i funkcjami matematycznymi Rozdział 8. Używanie tablic Rozdział 9. Używanie obiektów Rozdział 10. Praca z XML Rozdział 11. Praca z wyrażeniami regularnymi
164
Część II Praca z obiektami ActionScript 3.0
Rozdział 6. Używanie łańcuchów znaków
165
Rozdział 6.
Używanie łańcuchów znaków W tym rozdziale:
Tworzenie i edytowanie łańcuchów znaków
Łączenie wielu łańcuchów w jeden łańcuch znaków
Konwertowanie wielkości znaków w łańcuchu
Zmiana pojedynczych znaków w łańcuchu
Dzielenie łańcuchów na mniejsze części
Konwertowanie obiektów na łańcuchy i z łańcuchów znaków
Łańcuchem znaków we Flashu może być dowolny fragment danych tekstowych — od litery, poprzez wyraz, aż po wiele akapitów. Łańcuchy mogą także składać się wyłącznie z cyfr lub innych znaków niebędących literami, jak np. numer telefonu. Jeśli chodzi o łańcuchy znaków, to ActionScript 3.0 oferuje wiele sposobów pracy, manipulowania, wyszukiwania oraz przekształcania. W rozdziale tym opiszemy niektóre operacje dostępne dla tego typu tekstowego.
Praca z prostymi łańcuchami znaków Większość łańcuchów, jakim przyjrzeliśmy się do tej pory, była prostymi łańcuchami znaków lub fragmentami tekstu otoczonymi znakami cudzysłowu, dodanymi bezpośrednio w kodzie. Jak już wiesz z rozdziału 2., w ActionScript 3.0 wszystko jest obiektem, także łańcuchy znaków. Gdy zapiszesz łańcuch "Witaj świecie!", w rzeczywistości utworzysz instancję klasy String. Klasa String poza możliwością przechowywania tekstu oferuje ogromną funkcjonalność. Chociaż między łańcuchem znaków utworzonym za pomocą new String() a łańcuchem utworzonym za pomocą literału łańcuchowego jest techniczna różnica, kompilator automatycznie konwertuje literały łańcuchowe na obiekty łańcuchowe. Z tego też powodu poniższy kod jest całkowicie poprawny: "banan".length; // 5
166
Część II Praca z obiektami ActionScript 3.0
Konwertowanie obiektu łańcuchowego na typ prosty Jeśli zaistnieje taka potrzeba, możesz przekonwertować łańcuch tekstowy na jego prosty odpowiednik poprzez zastosowanie metody valueOf(). Właściwie jest to możliwe dla wszystkich obiektów. Rozwiązanie może być przydatne podczas pracy z prototypami, ale w powszechnym zastosowaniu używa się go bardzo rzadko. var s:String = "Witaj świecie!"; trace(s); // wyświetli: Witaj świecie! trace(s.valueOf()); // wyświetli: Witaj świecie!
Używanie sekwencji ucieczki Literały łańcuchowe używają cudzysłowów do wskazania kompilatorowi, że tekst umieszczony pomiędzy nimi powinien być interpretowany jako łańcuch znaków. Nie ma znaczenia, czy użyjesz cudzysłowów pojedynczych, czy podwójnych, liczy się natomiast sposób ich użycia. Powiedzmy, że chcesz utworzyć fragment tekstu zawierający w sobie cudzysłowy: var s:String = 'Porky mówi "That's all folks!"';
Którykolwiek z typów cudzysłowów zostanie użyty jako pierwszy, będzie typem poszukiwanym przez kompilator w celu zakończenia łańcucha znakowego. Przykład ten spowodowałby wystąpienie błędu w kodzie, ponieważ kompilator odczytałby go jako łańcuch znaków 'Porky mówi "Thats', po którym występuje jakiś bezsensowny kod. W takich sytuacjach musisz używać znaków ucieczki. Sekwencje ucieczki są zestawami specjalnych pseudoznaków. Kompilator zastępuje je prawdziwymi znakami, które normalnie są trudne do zapisania lub mogłyby uszkodzić kod. Są one zawsze poprzedzone znakiem \ (backslash). Sekwencje ucieczki dla podwójnego i pojedynczego cudzysłowu to odpowiednio \" i \'. Oto przykład: var s:String = 'Bugs mówi \"What\'s up, doc?\"'; trace(s); // Bugs mówi "What's up, doc?"
Do dyspozycji masz jeszcze inne sekwencje ucieczki. Jedną z najbardziej przydatnych jest znak nowego wiersza, \n, który jest odpowiednikiem naciśnięcia klawisza Enter. Następujący kod: var s:String = "Kochana Mamo,\nWszystko w porządku. Tęsknię za Tobą.\nKochający\nMims";
Wyświetli: Kochana Mamo, Wszystko w porządku. Tęsknię za Tobą. Kochający Mims
Rozdział 6. Używanie łańcuchów znaków
167
Tabela 6.1. Kompletna lista dostępnych znaków ucieczki Sekwencja ucieczki
Wynikowy łańcuch znakowy
\b
Znak backspace.
\f
Znak wysunięcia strony. Ten znak powoduje przejście o jedną stronę dalej i używany jest bardzo rzadko.
\n
Znak nowego wiersza.
\r
Znak powrotu karetki. (Powrót karetki i nowy wiersz są do siebie bardzo podobne. Znak nowego wiersza przenosi kursor o jeden wiersz, podczas gdy powrót karetki przenosi kursor z powrotem na początek wiersza. W większości przypadków powinieneś używać znaku nowego wiersza).
\t
Znak tabulatora.
\unnnn
Wstawia wybrany przez Ciebie czterocyfrowy znak w szesnastkowym kodzie Unicode — np. \u0416 jest znakiem ż w cyrylicy (Ж).
\xnn
Wstawia wybrany przez Ciebie dwucyfrowy znak w szesnastkowym kodzie ASCII — np. \xA5 jest symbolem japońskiego jena (¥).
\'
Znak pojedynczego cudzysłowu (').
\"
Znak podwójnego cudzysłowu (").
\\
Znak backslash (\).
Konwersja na łańcuchy i z łańcuchów znakowych Ponieważ łańcuchy znaków mogą być z łatwością czytane przez ludzi, są one we Flashu preferowanym sposobem wyświetlania informacji na temat innych obiektów. Z tego też powodu każdy obiekt w ActionScript 3.0 implementuje metodę toString(), wyświetlającą jego wartość w postaci łańcucha znaków.
Użycie toString Powiedzmy, że chcesz wyświetlić bieżącą datę, wykorzystując w tym celu instancję klasy Date: var now:Date = new Date(); trace("Dzisiejsza data to " + now.toString()); // wyświetli: Dzisiejsza data to Wed Sep 10 16:14:37 GMT+0100 2008
Jak widzisz, data na wyjściu wygląda tak, jakby była łańcuchem tekstowym. W rzeczywistości w wielu sytuacjach metoda toString() jest wywoływana automatycznie. Jest to rodzaj niejawnej konwersji wbudowanej w ActionScript. Jeśli pominiesz metodę toString(), jak w następnym przykładzie, metoda trace() automatycznie ją wywoła dla każdego przekazanego parametru.
168
Część II Praca z obiektami ActionScript 3.0 trace(new Date()); // wyświetli: Wed Sep 10 16:25:57 GMT+0100 2008
Wiele klas, w tym również te tworzone przez Ciebie, nie wyświetli domyślnie zbyt wielu przydatnych informacji po przekonwertowaniu ich na łańcuchy znaków za pomocą toString(). Domyślnym zachowaniem dla większości obiektów jest wyświetlanie słowa „object”, po którym następuje nazwa ich klasy. Na przykład: trace(new Sprite()); // wyświetli: [object Sprite]
Na szczęście we własnych klasach możesz przesłonić to zachowanie, tak aby wyświetlane były określone przez Ciebie informacje. Dodaj w tym celu własną metodę toString() w definicji swojej klasy, zwracającą pewien łańcuch znaków. Nie zapomnij o słowie kluczowym override! Zdefiniuj klasę MySprite. package pl.helion.as3biblia { class MySprite extends flash.display.Sprite { override public function toString():String { return ("MySprite (" + name + ") - x: " + x + ", y: " + y); } } }
W kodzie głównym (w dowolnym miejscu poza klasą MySprite) użyj następującego kodu: var test:MySprite = new MySprite(); test.name = "foo"; test.x = 25; test.y = 100; trace(test); // wyświetli: MySprite (foo) - x: 25, y: 100
Czyż nie jest to bardziej przydatne od wyświetlenia tylko [object Sprite]? Zalecamy Ci wyrobienie sobie nawyku definiowania własnych metod toString() dla każdej nowo tworzonej klasy. Znacznie polepsza to czytelność kodu podczas debugowania.
Rzutowanie na typ String Teraz spróbujmy zrobić coś innego — przypiszmy do łańcucha znaków dzisiejszą datę: var now:Date = new Date(); var nowString:String = now; // Powoduje błąd kompilacji.
Co się stało? Dlaczego kod nie zadziałał? Czy now nie powinno zostać niejawnie przekonwertowane na łańcuch znaków? Błąd wynika z faktu, iż nowString jest obiektem typu String i oczekuje jako swojej wartości obiektu łańcuchowego. W takich przypadkach kompilator jest bardziej rygorystyczny względem tego, co jest łańcuchem znaków, i jesteśmy zmuszeni do ręcznego przekonwertowania tego obiektu na typ String. var nowString:String = String(now);
Przekonwertuje to obiekt typu Date na typ String i zwróci odpowiednik łańcuchowy zmiennej now, której wartość zostanie przypisana do nowString. Jeśli musisz odświeżyć swoją wiedzę na temat konwersji typów i rzutowania, zajrzyj do rozdziału 3.
Rozdział 6. Używanie łańcuchów znaków
169
Można by tutaj użyć również metody toString(): var nowString:String = now.toString();
Konwersja łańcuchów znaków na inne typy Klasa String może być również prawie bezboleśnie konwertowana na inne typy. Konwersja łańcucha znaków zawierającego tylko dane liczbowe wymaga jedynie rzutowania go na obiekt typu Number: var shoeSize:String = "12"; var iq:Number = Number(shoeSize);
W takim łańcuchu znaków znajdować się mogą tylko cyfry. Próba rzutowania innej wartości spowoduje przypisanie złośliwej wartości NaN (Not a Number): var dialASong:Number = Number("(718) 387-6962"); trace(dialASong); // wyświetli : NaN
Dodawanie łańcuchów znaków do liczb może również dawać dziwne wyniki, ponieważ kompilator przekonwertuje liczby na łańcuchy znaków, zamiast zrobić na odwrót. var a:Number = 2 + "2"; trace(a); // wyświetli: 22
Konwertowanie łańcuchów znaków na tablice Konwersja łańcucha znaków na tablicę może okazać się bardzo przydatna, zwłaszcza podczas przetwarzania odpowiedzi przychodzących z serwera potrafiącego wysyłać jedynie łańcuchy znaków. Do takich zadań klasa String wykorzystuje metodę split(). Metoda split() pobiera dwa argumenty. Pierwszy z nich to separator. Jest to znak, łańcuch znaków lub wyrażenie regularne używane do rozdzielania poszczególnych elementów tablicy. Drugi parametr jest opcjonalny i jest maksymalną liczbą elementów nowej tablicy. Poniżej przedstawiamy trzy przykłady zastosowania metody split(): var keywords:String = "ludzie,gliwice,przyjaciele,piknik"; var tags:Array = keywords.split(","); // tags = ["ludzie", "gliwice", "przyjaciele", "piknik"] var sentence:String = "Kto pod kim dołki kopie, ten sam w nie wpada"; var words:Array = sentence.split(" ", 4); // Ogranicz do czterech elementów. // words = ["Kto", "pod", "kim", "dołki"] var state:String = "Mississippi"; var foo:Array = state.split("ss"); // foo = ["Mi", "i", "ippi"]
Łączenie łańcuchów znaków Do łączenia łańcuchów znaków w całość klasa String oferuje metodę concat, pobierającą dowolną liczbę argumentów (konwertuje je w razie potrzeby na łańcuchy znaków) i zwracającą nowy łańcuch znaków składający się ze sklejonych ze sobą łańcuchów cząstkowych.
170
Część II Praca z obiektami ActionScript 3.0 var hello:String = "Dobry wieczór."; var firstName:String = "Mims"; var greeting:String = hello.concat(" Nazywam się ", firstName, ". Miło Cię poznać."); trace(greeting); // wyświetli: Dobry wieczór. Nazywam się Mims. Miło Cię poznać.
Do przeprowadzania konkatenacji w ActionScript można też używać operatora dodawania (+), który — jak zapewne zauważysz — w praktyce spisuje się o wiele lepiej. var greeting:String = "Witam, nazywam się " + firstName + ". Tak jest o wiele łątwiej!"; trace(greeting); // wyświetli: Witam, nazywam się Mims. Tak jest o wiele łatwiej!
Konwersja wielkości znaków w łańcuchu Aby przekonwertować łańcuch znaków na ten sam tekst zapisany wielkimi literami, wywołaj metodę toUpperCase(). Jak widać w poniższym przykładzie, spowoduje to zwrócenie nowego łańcucha znaków z tymi samymi literami, tyle że wielkimi. Znaki niebędące literami pozostaną bez zmian. var cleanser:String = "Ajax"; var webTechnology:String = cleanser.toUpperCase(); trace(webTechnology); // wyświetli: AJAX
Aby zamienić wszystkie litery na małe, użyj metody toLowerCase(): var loudText:String = "SŁYSZYSZ MNIE?"; var quietText:String = loudText.toLowerCase(); trace(quietText); // wyświetli: słyszysz mnie?
Wywołanie tych metod nie zmienia wielkości liter w samym łańcuchu, ale zwraca nowy łańcuch znaków ze zmienioną wielkością liter. Oryginał pozostanie bez zmian. Jeśli chcesz zmienić oryginał, musisz przypisać mu wartość zwracaną przez tę funkcję. Na przykład: var alert:String = "error"; alert = alert.toUpperCase(); // alert = ERROR
Użycie metody toUpperCase() lub toLowerCase() jest bardzo przydatne podczas porównywania dwóch łańcuchów znaków bez względu na wielkość liter, np. podczas sprawdzania hasła: var inputPW:String = "HaXoR" var storedPW:String = "haxor"; if (storedPW.toLowerCase() == inputPW.toLowerCase()) { trace("Logowanie zakończone sukcesem"); } else { trace("Błąd logowania"); } // wyświetli : Logowanie zakończone sukcesem
Rozdział 6. Używanie łańcuchów znaków
171
Używanie poszczególnych znaków łańcucha Podczas pracy z łańcuchami znaków może zaistnieć potrzeba uzyskania dostępu do konkretnego znaku w łańcuchu lub poznania jego łącznej liczby znaków. Być może łatwiej będzie Ci traktować łańcuch znaków jak tablicę zawierającą pojedyncze znaki (osobom programującym wcześniej w języku C może to wydawać się znajome). O ile łańcuch znaków w ActionScript nie jest tablicą, o tyle niektóre jego właściwości przypominają te dostępne dla tablic. Łańcuch znaków ma indeksy rozpoczynające się od 0 i zliczane w górę, a każdemu przypisany jest jeden znak łańcucha. W tym podrozdziale opisano sposoby dostępu do poszczególnych znaków wewnątrz łańcucha.
Uzyskanie liczby znaków w łańcuchu W celu określenia liczby znaków w obiekcie łańcuchowym możesz sprawdzić jego właściwość length. Właściwość ta działa tak samo jak właściwość length tablicy. var bigWord:String = "PNEUMONOULTRAMICROSCOPICSILICOVOLCANOCONIOSIS"; trace("Liczba liter: ", bigWord.length); // wyświetli : 45
length jest właściwością tylko do odczytu, co oznacza, że nie możesz zmienić jej wartości, chyba że do swojego łańcucha dodasz nowe znaki.
Dostęp do poszczególnych znaków W łańcuchu — w przeciwieństwie do tablic — nie możesz uzyskiwać dostępu do znaków za pomocą składni tablicowej: var firstName = "Norbert"; trace(firstName[1]);
Spowoduje to wystąpienie błędu. Do uzyskiwania dostępu do pojedynczych znaków użyj zatem metody charAt(), zwracającej znak oznaczony konkretnym indeksem: trace(firstName.charAt(1)); // wyświetli: o
Konwersja znaku na kod znaku W podobny sposób możesz za pomoą metody charCodeAt() przekonwertować znak znajdujący się na określonej pozycji na kod ASCII tego znaku. Okaże się to przydatne podczas pracy na liczbowych odpowiednikach znaków zamiast na samych znakach, np. podczas wywoływania obiektu nasłuchującego zdarzenia naciśnięcia klawisza (opisanego w rozdziale 17.). var band:String = "Mötley Crüe"; trace(band.charCodeAt(1)); // wyświetli 246 (kod znaku ö)
172
Część II Praca z obiektami ActionScript 3.0
Ten kod może zostać z powrotem przekonwertowany na literę poprzez wywołanie statycznej metody String.fromCharCode(), która zwraca przekonwertowany znak jako nowy obiekt String. var buttonPressed:Number = 88; trace("Użytkownik nacisnął klawisz ", String.fromCharCode(buttonPressed)); // wyświetli: Użytkownik nacisnął klawisz x.
Lista kodów ASCII dla poszczególnych znaków dostępna jest na stronie Wikipedii pod adresem http://pl.wikipedia.org/wiki/ASCII.
Wyszukiwanie w łańcuchu znaków Czasami może zajść potrzeba wyszukania fragmentu tekstu w łańcuchu znaków. W tym celu klasa String oferuje kilka metod.
Wyszukiwanie przez podłańcuch Podłańcuch jest dowolną mniejszą częścią tekstu wewnątrz łańcucha tekstowego. Np. „def” jest podłańcuchem „abcdefghi”. Za pomocą metod indexOf() oraz lastIndexOf() możesz szukać pierwszego indeksu podłańcucha, czyli pierwszej pozycji podłańcucha w tekście. Obie metody działają w ten sam sposób, poza tym że lastIndexOf() rozpoczyna od końca i przesuwa się wstecz, podczas gdy indexOf() pracuje, począwszy od pierwszego indeksu. Funkcja zwraca numer indeksu podłańcucha lub jeśli nie może go znaleźć, liczbę –1. Pamiętaj, że indeksy łańcuchów rozpoczynają się od 0. Poniżej sprawdzamy indeksy niektórych imion z listy: var names:String = "Joanna, Jakub, Janusz, Jan, Justyna"; trace(names.indexOf("Jakub")); // wyświetli: 8 trace(names.lastIndexOf("J")); // wyświetli: 28 trace(names.indexOf("Robert")); // wyświetli: -1
Obie metody pobierają dwa parametry. Pierwszy z nich to podłańcuch, który należy odnaleźć. Drugi parametr jest opcjonalny i określa indeks, od którego ma rozpocząć się poszukiwanie. Drugi parametr może okazać się bardzo przydatny, jeśli chcesz znaleźć wszystkie wystąpienia podłańcucha. Kolejny przykład szuka litery „a” w tekście, dopóki nie odnajdzie wszystkich jej wystąpień: var story:String = "Pogoda była ponura i deszczowa..."; var pattern:String = "a"; var count:int = 0; var startIndex:int = 0; while(story.indexOf(pattern, startIndex) != -1) { count++; startIndex = story.indexOf(pattern, startIndex) + 1; } trace(count); // wyświetli: 4
Metody indexOf() oraz lastIndexOf() zostały w ActionScript 3.0 dodane również do klasy Array.
Rozdział 6. Używanie łańcuchów znaków
173
Wyszukiwanie za pomocą wyrażeń regularnych W ActionScript 3.0 uaktualniono klasę String, dołączając poniższe metody przeszukiwania tekstu z wykorzystaniem wyrażeń regularnych. Metody te są o wiele potężniejsze i bardziej elastyczne od wyszukiwania za pomocą indexOf. Zalicza się do nich: — działa podobnie do indexOf w tym, że przeszukuje łańcuch znaków w celu znalezienia wystąpienia kryterium poszukiwania. Różnica polega na tym, że tutaj jako argumentu użyjesz wyrażenia regularnego. Metoda search umożliwia Ci szukanie łańcuchów znaków lub innych obiektów (które najpierw zostaną przekonwertowane na łańcuchy znaków). Jeśli nic nie zostanie znalezione, zwrócona zostanie wartość –1. W przeciwnym razie zwracany jest indeks pierwszego dopasowania.
search(pattern:*):int
match(pattern:*):Array — użyj metody match, aby zwrócić wszystkie dopasowania
konkretnego wzorca w postaci tablicy. Również tutaj jako argumenty zaakceptowane zostaną łańcuchy znaków oraz inne obiekty. — wyszukuje w łańcuchu znaków wzorca (zazwyczaj wyrażenia regularnego) i tworzy nowy łańcuch znaków, w którym każde dopasowanie zastąpione zostanie obiektem zastępczym (przeważnie typu String).
replace(pattern:*, replacement:Object):String
To jest zaledwie krótkie wprowadzenie do tych metod. Ponieważ na temat wyrażeń regularnych można długo dyskutować, postanowiliśmy poświęcić im odrębny rozdział. Zostały one szczegółowo opisane w rozdziale 11.
Dzielenie łańcuchów Czasami możesz potrzebować wydobyć mniejszy łańcuch z większego łańcucha. Powiedzmy, że chcesz np. odnaleźć osiem pierwszych znaków łańcucha lub pobrać wszystko, co znajduje się za dwukropkiem. ActionScript oferuje do tego celu trzy metody: slice(), substr() oraz substring(). Wszystkie trzy są bardzo podobne, ponieważ wszystkie zasadniczo zwracają podłańcuch obiektu łańcuchowego. Różnią się jedynie sposobem traktowania swoich dwóch opcjonalnych parametrów. slice(start:Number, end:Number):String
oraz substring(start:Number, end:Number):String
są prawie identyczne. Obie te metody zwracają wszystkie znaki znajdujące się pomiędzy indeksami określonymi przez parametry start oraz end. Jedyna różnica między nimi polega na tym, że slice() potrafi przyjmować jako parametry wartości ujemne, a substring() nie. Ujemne wartości (jak np. –3) spowodują liczenie wstecz, począwszy od końca łańcucha znaków. W przypadku obu poleceń pominięcie parametru końca spowoduje, że domyślnie użyty zostanie koniec łańcucha znaków. Jeśli pominięty zostanie parametr początku, domyślnie użyty zostanie początek łańcucha.
174
Część II Praca z obiektami ActionScript 3.0
Poniższy przykład szuka w łańcuchu znaków znaku spacji i używa jego indeksu do zwrócenia pierwszego wyrazu: var address:String = "Siedem lat temu..."; var firstSpace:int = address.indexOf(" "); var firstWord:String = address.slice(0, firstSpace); trace(firstWord); // wyświetli: Siedem var elipsis:String = address.slice(-3); // bierze trzy ostatnie znaki trace(elipsis); // wyświetli: ...
Metoda substr() trochę różni się od slice() oraz substring(). Zamiast pobierać indeks końcowy, używa parametru długości. Rozpoczyna od podanego indeksu początkowego i zwraca łańcuch znaków, którego długość odpowiada podanej wartości parametru length. Na przykład: trace(address.substr(7, 3)); // wyświetli: lat
Ponieważ w większości sytuacji wszystkie te metody zachowują się bardzo podobnie, używaj tej, która będzie Ci się wydawać najbardziej odpowiednia dla danego zastosowania. Zalecamy jednak używanie slice() zamiast substring(), ponieważ potrafi więcej i nie jest tak łatwa do pomylenia z substr().
Podsumowanie
Łańcuch znaków może być dowolnym fragmentem tekstu. Do odróżniania łańcuchów od wykonywalnego kodu służą cudzysłowy.
Klasa String oferuje dodatkową funkcjonalność. Wszystkie łańcuchy znaków są instancjami klasy String, nawet jeśli są literałami łańcuchowymi.
Tworzenie własnych metod toString() w swoich klasach pomoże w zrozumieniu zawartych w nich danych podczas przeglądania logów lub wyników zwracanych przez funkcję trace().
Łańcuchy znaków mogą być cięte lub łączone, można też uzyskiwać dostęp do ich poszczególnych znaków, podobnie jak to się ma w przypadku tablic.
Łańcuchy w ActionScript rozróżniają wielkość liter. Konwersja wielkości liter za pomocą metod toUpperCase() oraz toLowerCase() jest bardzo prosta.
Przeszukiwanie łańcuchów znaków i zastępowanie tekstu wewnątrz łańcuchów jest teraz o wiele prostsze dzięki nowym metodom oraz dołączeniu obsługi wyrażeń regularnych.
Rozdział 7.
Praca z liczbami i funkcjami matematycznymi W tym rozdziale:
Używanie klas liczbowych
Obliczenia arytmetyczne i trygonometryczne
Praca z datami i czasem
Bez dobrej obsługi liczb nie ma możliwości pisania zaawansowanych programów. Nie ma zatem nic dziwnego w tym, że ActionScript 3.0 umożliwia przeprowadzanie wszelkiego rodzaju obliczeń, od prostej arytmetyki po obsługę daty i czasu. Dzięki informacjom zamieszczonym w tym rozdziale będziesz mógł rozpocząć tworzenie pożytecznych, interaktywnych i estetycznych aplikacji.
Typy liczbowe Programując w ActionScript, możesz tworzyć liczby różnych typów. Aby zrozumieć sposób ich wykorzystania, musisz się cofnąć i przyjrzeć zbiorom liczb oraz temu, co reprezentują. Następnie zapoznasz się z reprezentacją różnych rodzajów liczb w nowoczesnych komputerach. Poznanie tych szczegółów implementacyjnych może pomóc czujnemu programiście zrozumieć, jakie typy danych są odpowiednie w danej sytuacji, zrozumieć ograniczenia języka oraz powszechnie występujące problemy.
Zbiory liczb Liczba to liczba, nieprawda? Nie do końca. Występuje kilka różnych typów liczb. Te typy liczb definiowane są poprzez zbiór wartości, jaki mogą reprezentować.
176
Część II Praca z obiektami ActionScript 3.0
Najbardziej znany jest zbiór liczb naturalnych, oznaczany przez . Zawiera on liczby całkowite, począwszy od zera: {0, 1, 2, 3...}. Liczb tych używasz do liczenia odrębnych obiektów: „Mam dwoje rodziców”. „Na tym lotnisku znajdują się cztery wyjścia bezpieczeństwa”. „Fortepian ma 88 klawiszy”. Powiększonym zbiorem liczb naturalnych jest zbiór liczb całkowitych, oznaczany przez . Zbiór ten zawiera liczby całkowite, zarówno ujemne, jak i dodatnie wraz z liczbą 0: {... –2, –1, 0, 1, 2...}. Jeszcze większym zbiorem jest zbiór liczb rzeczywistych, oznaczany przez . Liczby rzeczywiste reprezentują zarówno liczby wymierne, jak i niewymierne — czyli takie, które można zapisać w postaci ułamka, i takie, których nie można zapisać w ułamku. Nie ma możliwości wypisania podzbioru liczb rzeczywistych, ponieważ pomiędzy każdymi dwoma liczbami rzeczywistymi znajduje się nieskończona liczba innych liczb rzeczywistych. Przykładami liczb rzeczywistych są: –10; 6⅓; 3,14159265... oraz 4,4. (Z tych przykładów niewymierna jest liczba π, przybliżona tutaj do wartości 3,14159265... Liczba π czytana jako „pi”, nie może być przedstawiona dokładnie za pomocą ułamka zwykłego czy rozszerzenia dziesiętnego). Każda liczba, którą można zapisać w postaci ułamka zwykłego lub dziesiętnego, jest liczbą rzeczywistą. Za pomocą liczb rzeczywistych możesz mierzyć np. odległości, kąty, współczynniki pH czy kilogramy ciasteczek.
Reprezentacja liczb Pisząc liczby, tworzysz reprezentację ich wartości za pomocą cyfr. Np. koncepcja kryjąca się za liczbą sto to cyfra jeden poprzedzająca dwie cyfry zero. Możesz ją jednak przedstawić również jako sto kresek, 1×102, rzymską liczbę C czy jako dziesięć wierszy po dziesięć jedynek. Przy odrobinie wyobraźni można by wymyślić jeszcze wiele innych reprezentacji pojedynczej liczby. Liczby powszechnie zapisujemy w systemie dziesiętnym, mającym za podstawę liczbę 10. W systemie tym występuje dziesięć cyfr, od 0 do 9. Liczba 342 to trzy setki, cztery dziesiątki i dwie jedności. Podczas programowania możesz spotkać się z wykorzystaniem innych podstaw, najczęściej podstawy 16, zwanej powszechnie szesnastkową. W systemie szesnastkowym występuje 16 podstawowych znaków, cyfry od 0 do 9, a po nich występują litery A, B, C, D, E oraz F. Liczba 12 zapisana szesnastkowo oznacza jedną szesnastkę plus dwie jedynki: w sumie osiemnaście. Ponieważ jednak znaki od 0 do 9 wyglądają w obu systemach jednakowo, nie ma możliwości rozróżnienia, czy liczba zapisana jako 12 podana została w systemie szesnastkowym, gdzie oznacza osiemnaście, czy może w systemie dziesiętnym, gdzie jej wartość wynosi dwanaście. Aby móc rozróżnić notację szesnastkową od dziesiętnej, zazwyczaj poprzedza się liczby szesnastkowe przedrostkiem 0x. Liczba 0x2A oznacza dwie szesnastki plus A (dziesięć) jedynek, co daje dziesiętną wartość 42. W jakim celu używa się liczb szesnastkowych? Gdybyś spróbował porozmawiać z komputerem, zauważyłbyś, że komputery nie rozumieją liczb dziesiętnych. Rozumieją jedynie system liczbowy o podstawie 2, inaczej nazywany binarnym, gdzie jedynymi znakami są 0 oraz 1. Zapisywanie liczb w systemie dwójkowym wymaga większego nakładu pracy i więcej miejsca. Oczywiście zdanie to jest prawdziwe dla ludzi. Np. 2008 wyrażone
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
177
w postaci binarnej to 11111011000. System szesnastkowy natomiast jest zarówno zwięzły, jak i kompatybilny z systemem dwójkowym. Każdy znak w systemie szesnastkowym jest reprezentowany przez cztery znaki systemu binarnego, ponieważ 16 równa się 24. Dlatego 2008 można zapisać jako 0x7D8, co jest zapisem o wiele bardziej zwięzłym, pomijając miejsce potrzebne na 0x. W ActionScript 3.0 system szesnastkowy jest używany najczęściej do zapisywania 32-bitowych wartości kolorów, co pokazano na rysunku 7.1.
Cyfrowe reprezentacje liczb Przedstawienie liczby w komputerze wiąże się z kilkoma wyzwaniami. Po pierwsze, komputery reprezentują wszystko w postaci binarnej, a po drugie, sposób pracy procesora ogranicza liczbę znaków binarnych, składających się na jedną wartość.
Liczby całkowite bez znaku Najprostszym typem liczb do przedstawienia w postaci cyfrowej są liczby naturalne. Liczby naturalne (dodatnie liczby całkowite) są w komputerach reprezentowane przez liczby całkowite bez znaku. Typ całkowity w ActionScript 3.0 jest 32-bitowy. Oznacza to, że występują w nim 32 pozycje na znaki binarne. Aby zrozumieć to ograniczenie, porównaj to z liczbami dziesiętnymi. Największa liczba, jaką możesz zapisać w systemie dziesiętnym za pomocą trzech znaków, to 999 i jest o jeden mniejsza od 1000 lub 103. Zatem największa liczba, jaką możesz zapisać w systemie dwójkowym na 32 pozycjach, to: 1111 1111 1111 1111 1111 1111 1111 1111
i jest o jeden mniejsza od 1 0000 0000 0000 0000 0000 0000 0000 0000
lub 232. Oznacza to, że największa liczba, jaką możesz zapisać w postaci 32-bitowej liczby całkowitej bez znaku, to 232–1, co odpowiada dziesiętnej wartości 4 294 967 295. Cztery miliardy to zapewne duża liczba, ale łatwo sobie wyobrazić sytuację, w której okaże się niewystarczająca, jak chociażby podczas przedstawienia liczby ludności na świecie czy rocznej liczby odwiedzin niektórych witryn internetowych. Musisz pamiętać o tym, że liczby reprezentowane w komputerach są ograniczone. Zgodnie z tym, co pokazaliśmy, maksymalna wartość liczby całkowitej bez znaku jest ograniczona bezpośrednio poprzez rozmiar tej liczby w pamięci — w ActionScript 3.0 są to 32 bity.
Liczby całkowite ze znakiem Liczby całkowite (liczby całkowite, które mogą być zarówno ujemne, jak i dodatnie) są w komputerach reprezentowane przez liczby całkowite ze znakiem. Określenie „ze znakiem” służy do podkreślenia faktu, iż liczby te noszą w sobie informację odnośnie do swojego znaku. Informują Cię, czy są dodatnie, czy ujemne. Liczby bez znaku nie zawierają w sobie informacji o znaku. Nie wiemy więc, czy są dodatnie, czy może ujemne. Tylko poprzez przyjętą konwencję kojarzymy je jako liczby dodanie, podobnie jak oglądając mapę, zakładamy, że u góry znajduje się północ.
178
Część II Praca z obiektami ActionScript 3.0
Jeśli jednak podczas reprezentowania liczb dodatnich i ujemnych do dyspozycji masz tylko 32 znaki binarne, to jak przedstawisz zarówno liczby dodatnie, jak i ujemne? Rozwiązanie jest proste, a zarazem sprytne. Znamy dwa znaki, dodatni i ujemny. Binarny znak może przyjmować dwie wartości: 0 oraz 1. Zatem jeśli zarezerwujesz jeden z tych 32 bitów na znak, pozostanie Ci 31 bitów do przechowania wartości liczby. Taki zabieg wpływa oczywiście na maksymalną bezwzględną wartość, jaka może zostać przedstawiona na 32 bitach. Jeśli zarezerwujemy jeden bit na znak, to dla liczby całkowitej ze znakiem będziemy mieć 31 bitów na przedstawienie jej bezwzględnej wartości. Liczba binarna składająca się z 31 bitów może przyjąć maksymalną wartość 231–1, co jest wartością bliską 2 miliardom. Wskutek zabrania jednego znaku binarnego maksymalna wartość zmniejszy się o połowę. Ale zaczekaj! Poprzez dodanie znaku podwajasz liczbę wartości, jakie mogą zostać przedstawione! Każda liczba, która wcześniej była wartością bez znaku (poza zerem), może teraz mieć dwie wartości, dodatnią i ujemną. Liczby całkowite ze znakiem zamiast sięgać tak wysokich wartości jak liczby bez znaku, sięgają jedynie do połowy wartości w górę, ale również do połowy wartości w dół. Minimalna wartość 32-bitowej liczby całkowitej bez znaku wynosi 0, a minimalna wartość 32-bitowej liczby całkowitej ze znakiem wynosi –2 147 483 648. Bit znaku jest przechowywany jako najbardziej znaczący bit spośród 32. Oprócz tego w konwencjonalnych językach programowania, jak ActionScript 3.0, dostępna jest sztuczka umożliwiająca procesorowi dodanie dwóch liczb w ten sam sposób, niezależnie od tego, jakiego są znaku. Sztuczka ta nazywa się uzupełnieniem do dwóch i polega na liczeniu binarnym wstecz dla pozycji niebędących znakiem w liczbach ujemnych. Np. 4-bitowa liczba całkowita ze znakiem dla –1 ma postać 1111, –2 ma postać 1110 itd. Wtedy dodanie dodatniej i ujemnej liczby jest zwykłym dodawaniem binarnym ignorującym przepełnienie. Wspominamy o tym, ponieważ w arytmetyce liczb całkowitych przepełnienie jest ignorowane. Jeśli zatem nie będziesz ostrożny w przypadkach brzegowych, możesz uzyskać zwodnicze wyniki, będące rezultatem przepełnienia. Musisz też pamiętać o tym podczas obliczeń bitowych na liczbach całkowitych ze znakiem.
Liczby zmiennoprzecinkowe Liczby rzeczywiste reprezentowane są jako liczby zmiennoprzecinkowe. Podobnie jak notacja naukowa może przedstawiać zwięźle ogromny zakres liczb w postaci a×10b, tak liczby zmiennoprzecinkowe posiadają niewiarygodny zakres, a ich dokładność znajduje się w proporcji do skali samej wartości. Np. 5,12×10-5 jest niewyobrażalnie małą i precyzyjną liczbą 0,0000512, a 5,12×109 jest bardzo dużą i precyzyjną liczbą, 5 120 000 000. Obie te liczby zapewniają odpowiednią precyzję dla swojej własnej skali. Przy podawaniu wagi planety jedna tysięczna grama nie ma znaczenia, ale podczas odmierzania składów chemicznych będziesz potrzebował takiej dokładności. Liczby zmiennoprzecinkowe dzielą dostępne bity tak, aby przechowywały bit znaku, mantysę i cechę. Jeśli liczbę zmiennoprzecinkową przedstawić za pomocą wyrażenia a×Pb, to można ją zdefiniować jako: iloczyn mantysy (a) i podstawy systemu liczbowego (P) podniesiony do potęgi określonej przez cechę (b). ActionScript 3.0 używa standaryzowanej implementacji liczb zmiennoprzecinkowych określonej przez IEEE 754, czyli standard reprezentacji binarnej i operacji na liczbach zmiennoprzecinkowych. W tej implementacji a oraz b są binarne, a podstawą systemu P jest liczba 2.
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
179
Typ zmiennoprzecinkowy w AS3, przedstawiony w poniższym materiale, jest 64-bitową liczbą zmiennoprzecinkową podwójnej precyzji IEEE 754. Rezerwuje 52 bity na część ułamkową oraz 11 bitów na cechę. Jeden bit pozostaje na znak. Struktura tego rozmiaru może reprezentować niewiarygodnie małe (w wartości bezwzględnej) oraz duże liczby z dużą dokładnością. Przedstawienie wszystkich liczb rzeczywistych w komputerze z perfekcyjną dokładnością nie jest możliwe, dlatego liczby zmiennoprzecinkowe raczej przybliżają wartości, jednak dokładność oferowana przez liczby zmiennoprzecinkowe podwójnej precyzji powinna być wystarczająca dla prawie wszystkich zastosowań. Używaj liczb zmiennoprzecinkowych do reprezentowania liczb rzeczywistych oraz bardzo dużych liczb dodatnich i ujemnych. Maksymalna wartość, jaką AS3 może przedstawić za pomocą liczby zmiennoprzecinkowej podwójnej precyzji, wynosi około 1,8×10308. Trudno znaleźć przykład tak dużej liczby. Z grubsza to 1 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 razy tyle, ile jest atomów w widocznym wszechświecie.
Używanie liczb w ActionScript W ActionScript 3.0 są trzy podstawowe typy dla liczb, odpowiadające trzem sposobom cyfrowego reprezentowania liczb, które omówiliśmy wcześniej. Daje to programiście możliwość wyboru najlepszego typu liczbowego dla danego zadania. Te typy liczbowe zawierają specjalne wartości chroniące Twój program przed przerwaniem jego działania w niektórych skrajnych przypadkach. Oprócz tego język pozwala na wpisywanie liczb bezpośrednio w kodzie. Poprzednie wersje ActionScript bazowały na pojedynczym typie dla wszystkich wartości numerycznych, ponieważ liczby zmiennoprzecinkowe mogą reprezentować zarówno liczby całkowite, jak i ułamkowe. ActionScript 3.0 wprowadza typy dla liczb całkowitych ze znakiem i bez znaku.
Number Typ Number jest liczbą zmiennoprzecinkową podwójnej precyzji zgodną ze standardem IEEE 754 i może reprezentować dodatnie i ujemne liczby, zarówno całkowite, jak i ułamkowe. Liczby typu Number nadają się dla prawie każdego rodzaju wymiarów i są jednym z koni pociągowych w ActionScript 3.0. Domyślna wartość dla zmiennej typu Number bez przypisanej wartości to NaN i zostanie bliżej opisana w punkcie „Przypadki brzegowe” w dalszej części tego rozdziału. Liczbom typu Number zazwyczaj nadaje się wartość poprzez przypisanie im wartości literału liczbowego. Klasa Number posiada jednak także konstruktor, który spróbuje przekonwertować obiekt przekazany do liczby i przypisać go do nowej liczby typu Number: var n:Number; trace(n); // NaN n = 12; trace(n); // 12
180
Część II Praca z obiektami ActionScript 3.0 Przypisanie jest typowym sposobem połączenia wartości ze zmienną. Rzadko kiedy spotyka się użycie konstruktora typu liczbowego.
Używaj liczb typu Number dla każdego rodzaju wymiarów, wartości mogących zawierać części ułamkowe oraz dla ekstremalnie dużych i małych wartości.
int Typ int jest nowością w ActionScript 3.0. Jest on 32-bitową liczbą całkowitą ze znakiem. Pomimo pisowni małą literą int jest klasą podobnie jak inne typy. I podobnie jak typ Number też posiada konstruktor, który używany jest równie rzadko. Możesz utworzyć nową liczbę typu int poprzez zwykłe zapisane literału liczbowego. Jeśli spróbujesz przypisać do zmiennej int liczbę niecałkowitą, część ułamkowa zostanie odrzucona. Domyślna wartość dla nieprzypisanej wartości int to 0. Wydajność zmiennych całkowitych wewnątrz programu Flash Player jest o wiele wyższa od wydajności liczb zmiennoprzecinkowych, dlatego możesz używać typów liczb całkowitych we fragmentach kodu wymagających bardzo szybkiego wykonania oraz tam, gdzie części ułamkowe nie są potrzebne. Typ int idealnie spisuje się w roli liczników i często spotyka się go w pętlach: for (var i:int = 0; i < 1000; i++)
uint Kolejny nowy typ liczbowy w ActionScript 3.0 to uint, reprezentujący 32-bitowe liczby całkowite bez znaku. Również jest klasą i posiada rzadko używany konstruktor. Podobnie jak w przypadku int, domyślną wartością tego typu jest 0. Typ uint poza zakresem wartości jest identyczny z typem int. Typ uint jest używany dla bitowych operacji matematycznych, ponieważ jego wartość jest zawsze prostą binarną translacją jego wartości, bez specjalnego kodowania czy reguł takich jak liczenie wstecz w dopełnieniu dwójkowym. Oferuje wolny dostęp do wszystkich 32 bitów wartości w pamięci. Dobrym przykładem zastosowania tego typu są wartości kolorów, przedstawione na rysunku 7.1, gromadzące informacje na temat przezroczystości oraz zawartości koloru czerwonego, zielonego i niebieskiego w pojedynczym 32-bitowym polu. Rysunek 7.1. Wartość koloru reprezentowana w pojedynczej liczbie uint
Powyższa wartość przedstawia kryjący szary kolor. Ponieważ wykorzystuje 32 bity, nie może zostać zapisana w liczbie całkowitej ze znakiem, która używa 1 bitu na znak i pozostawia do wykorzystania 31.
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
181
Nie powinieneś używać typu uint dla liczb całkowitych lub liczników, ponieważ nie jest on w stanie reprezentować liczb ujemnych. Nawet jeśli wiesz, że liczba całkowita, którą chcesz przedstawić, nie zejdzie poniżej zera, warto pozostawić ją jako liczbę ze znakiem. W przeciwnym razie łatwo będzie popełnić jakieś proste błędy. Używaj typu uint do operacji bitowych, a int dla pozostałych liczb całkowitych. Oprócz tego warto pamiętać o tym, że liczby całkowite zachowują się dziwnie, gdy przekroczą swoje ograniczenia, przechodząc na przeciwny znak w przypadku liczb całkowitych ze znakiem. Liczby całkowite bez znaku przechodzą pomiędzy zero a maksymalną wartość, podczas gdy liczby typu Number w miarę szybkiego wzrostu stają się coraz mniej dokładne. Nie używaj typów int oraz uint dla wartości, co do których istnieje najmniejsze prawdopodobieństwo przekroczenia zakresu, albo sprawdzaj zawsze te wartości. Ogólnie rzecz biorąc, możesz używać typu Number dla wszystkich typów liczb, ponieważ jest najbardziej elastyczny.
Literały Użycie literałów liczbowych jest proste. Najprostszym sposobem wpisania liczby bezpośrednio w kodzie jest zapisanie jej w zwykłej, dziesiętnej notacji. Przykładami poprawnych literałów dziesiętnych są: 1337 -4; .8; 0.333; -1.414;
Jak już wcześniej wspomnieliśmy, liczby szesnastkowe poprzedza się przedrostkiem 0x. Aby ActionScript zinterpretował liczbę jako szesnastkową, musisz zrobić to samo: var foo:uint = 0x12AB;
Do deklarowania liczb możesz też używać notacji wykładniczej. Notacja wykładnicza wyraża liczbę jako liczbę rzeczywistą mnożoną przez potęgę liczby dziesięć. Zazwyczaj spotyka się notację naukową, w której liczba rzeczywista mieści się w przedziale od 1 do 10. Taki rodzaj reprezentacji pozwoli Ci skupić się na względnych rozmiarach liczb bez wyrażania ich w postaci długich łańcuchów cyfr, szczególnie dla bardzo małych lub bardzo dużych liczb, jak np. 6,02×1023. Używając w kodzie notacji wykładniczej, będziesz używać znaku e do przedstawiania podstawy oraz do wskazania, że wartość następująca po niej jest wykładnikiem. Ta sama liczba zapisana zostałaby w postaci 6.02e23. Poniżej przedstawiamy kilka poprawnych literałów wykładniczych: 2.007e3; // 2007 1414e-3; // 1.414 1.9e+17; // 190 000 000 000 000 000
We wcześniejszych wersjach ActionScript można było również wpisywać literały liczbowe w systemie ósemkowym (oktalnym) poprzez poprzedzenie ich dodatkowym 0. Funkcja ta, jak można się domyślać, z powodu licznych omyłek nie przyniosła notacji ósemkowej wielu sukcesów; została usunięta z ActionScript 3.0.
182
Część II Praca z obiektami ActionScript 3.0
Przypadki brzegowe Każdy typ ActionScript zawiera wartości specjalne dołączane do jego potencjalnego zakresu. Są one bardzo przydatne, ponieważ chronią program przed wytwarzaniem błędów związanych z wystąpieniem jednego z przypadków brzegowych.
Not a number Stała NaN reprezentuje „not a number”, nienumeryczną wartość dołączoną do typu Number. NaN znajduje się w instancjach Number, którym nie przypisano żadnej wartości lub dla których wystąpił błąd podczas próby konwersji. Operacje matematyczne z nierzeczywistymi lub niezdefiniowanymi wynikami, jak chociażby pierwiastek kwadratowy z –1 lub zero dzielone przez zero, również zwracają NaN. Porównania z NaN zawsze zwrócą false, a większość operacji matematycznych na NaN zwraca NaN. Gdy chcesz sprawdzić, czy liczba jest zdefiniowana, nie próbuj porównywać jej wartości do samego NaN. Zamiast tego użyj funkcji najwyższego poziomu isNaN(): var n:Number; trace(n); // NaN trace(isNaN(n)); // true trace(n == NaN); // false! Dlatego używa się isNan() n = Number("to nie przekonwertuje się na liczbę"); trace(isNaN(n)); // true n = 10; trace(isNaN(n)); // false
We wcześniejszych wersjach ActionScript różne błędy mogły powodować przypisanie liczbie wartości NaN lub undefined. W ActionScript 3.0 instancje Number mogą mieć wartość NaN lub być liczbami rzeczywistymi, ale nigdy nie mogą mieć wartości undefined lub void.
Nieskończoność Typ Number posiada również specjalne wartości stałe dla dodatniej i ujemnej nieskończoności: Number.POSITIVE_INFINITY oraz Number.NEGATIVE_INFINITY. Jeśli otrzymasz nieskończoność, np. w wyniku dzielenia liczby typu Number przez zero, zamiast błędu przepełnienia lub błędu czasu wykonywania, liczba typu Number przyjmie jedną z tych specjalnych wartości nieskończoności. Możesz sprawdzać warunki nieskończoności za pomocą porównań, a porównania te będą działać zgodnie z Twoimi oczekiwaniami. Każda skończona liczba jest mniejsza niż dodatnia nieskończoność i większa niż ujemna nieskończoność, a nieskończoności są sobie równe. var n:Number = 10 / 0; trace(n); // Infinity trace(n == Number.POSITIVE_INFINITY); // true
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
183
Wartości minimalna i maksymalna Pomijając nieskończoność, występują fizyczne ograniczenia nałożone na rozmiar liczb, wynikające z ich implementacji w maszynie wirtualnej ActionScript. Pisaliśmy np. o tym, że maksymalna wartość 32-bitowej liczby całkowitej bez znaku wynosi 232–1. Te ograniczenia są udokumentowane we wszystkich trzech klasach liczbowych za pomocą statycznych stałych MAX_VALUE oraz MIN_VALUE. Stałe te odwołują się ogólnie do największej i najmniejszej możliwej wartości dla typów int oraz uint. Dla typu Number odwołują się do największej dodatniej skończonej liczby oraz do najmniejszej niezerowej, nieujemnej liczby, jaką da się przedstawić. trace(uint.MIN_VALUE); // 0 trace(uint.MAX_VALUE); // 4294967295 trace(int.MIN_VALUE); // -2147483648 trace(int.MAX_VALUE); // 2147483647 trace(Number.MIN_VALUE); // 4.9406564584124654e-324 trace(Number.MAX_VALUE); // 1.79769313486231e+308
NaN oraz Infinity są koncepcjami stosowanymi wyłącznie dla typu Number. Liczby całkowite, ze znakiem lub bez, nie posiadają tych zabezpieczeń.
Manipulowanie liczbami W ActionScript 3.0 liczby każdego typu cechują się dużą elastycznością i mogą służyć do dowolnego celu. Jeśli konieczne okażą się pewne manipulacje na typach liczbowych, nie będzie to wielkim problemem. Rzutowanie i konwersja są w AS3 naprawdę łatwe.
Konwersje liczbowe Nie musisz przejmować się konwersją liczb, gdy aby uzyskać liczbę całkowitą, mnożysz liczbę całkowitą przez zmiennoprzecinkową lub gdy chcesz dodać 0,1 do liczby całkowitej i uzyskać wynik w postaci zmiennoprzecinkowej. ActionScript 3.0 automatycznie przeprowadza w wyrażeniach konieczne konwersje pomiędzy liczbami zmiennoprzecinkowymi a liczbami całkowitymi. Bazuje przy tym na lewej stronie wyrażenia: do jakiego typu zostanie przypisana lub jako jaki typ zostanie zwrócona. Jeśli nie jest zadeklarowane, jakiego typu wynik wyrażenie powinno zwrócić, to jeśli jakakolwiek część tego wyrażenia jest komponentem zmiennoprzecinkowym, jego wynik również jest zmiennoprzecinkowy: var i:int = 3 * 2.04; // pomnożenie liczby całkowitej przez zmiennoprzecinkową, przypisanie // do liczby całkowitej trace(i); // 6 (to nadal liczba całkowita!) var n:Number = i + 0.1; // dodanie liczby całkowitej do zmiennoprzecinkowej, przypisanie // do liczby zmiennoprzecinkowej trace(n); // 6.1 (to liczba zmiennoprzecinkowa!) var x = 2 / 3; // dzielenie liczby całkowitej przez całkowitą; wynik dzielenia zostanie zamieniony na liczbę // zmiennoprzecinkową, ponieważ nie jest liczbą całkowitą, a lewa strona wyrażenia nie określa typu trace(x); // 0. 6666666666666666 (to liczba zmiennoprzecinkowa!)
184
Część II Praca z obiektami ActionScript 3.0
Konwersje łańcuchów znaków Prawdopodobnie najbardziej przydatnym rodzajem konwersji dla liczb jest ta na obiekty String i z obiektów typu String. Możesz dołączać liczby w wiadomościach, formatować je i wczytywać po wprowadzeniu przez użytkownika. Na szczęście proces ten jest tak prosty jak rzutowanie i w wielu przypadkach zostanie obsłużony automatycznie. Jeśli chcesz przekonwertować liczbę na łańcuch znaków, zrób to na jeden ze sposobów:
Wywołaj metodę toString() dla liczby. Jest to sposób preferowany.
Przeprowadź jawne rzutowanie na typ String. Spowoduje to wywołanie funkcji konwertującej najwyższego poziomu String(), która zinterpretuje liczbę jako łańcuch znaków.
Dołącz zmienną liczbową w wyrażeniu tekstowym. Spowoduje to niejawne przekonwertowanie liczby na łańcuch znaków: for (var i:int = 99; i > 0; i--) { trace(i + " drzwi w murze"); }
Wywołaj na zmiennej liczbowej jedną z metod formatujących w specjalny sposób, aby zamienić ją na łańcuch znaków w notacji niedziesiętnej. Wszystkie trzy typy liczb posiadają metody toExponential(), toFixed() oraz toPrecision() służące do sformatowania liczby do notacji wykładniczej lub stałoprzecinkowej.
Jeśli chcesz uzyskać liczbę z wartości łańcuchowej, wystarczy jawnie rzutować łańcuch znaków na typ liczbowy. Aby otrzymać liczbę z łańcucha, który może zawierać jeszcze inny tekst, ignorując przy tym pozostały tekst, możesz użyć funkcji najwyższego poziomu parseInt() oraz parseFloat(). Zwróci to interpretacje liczb całkowitych i rzeczywistych liczbowej zawartości łańcucha. Funkcje parseInt() oraz parseFloat() umożliwiają interpretowanie tekstu jako liczby o dowolnej podstawie. Poniższy fragment kodu porównuje sposoby konwertowania łańcuchów na liczby: trace(parseInt("3.14")); // 3 trace(int("3.14")); // 3 trace(parseFloat("3.14")); // 3.14 trace(Number("3.14")); // 3.14 trace(parseFloat("3.14 posiada długi ogon")); // 3.14 trace(Number("3.14 posiada długi ogon ")); // NaN trace(parseFloat("Co słychać 3.14?")); // NaN
Ostatni wiersz nie działa, ponieważ parseInt() oraz parseFloat() wymagają, aby liczba była pierwszym elementem łańcucha, co znacznie ogranicza przydatność tych funkcji. Z tych dwóch podejść osobiście polecam używanie metod jawnego rzutowania. Ponieważ naprawdę ma miejsce zamiana typów, rzutowanie w kodzie wygląda lepiej niż globalna funkcja parseInt(). Możesz użyć wyrażeń regularnych do łatwego parsowania liczb ze złożonych łańcuchów znaków i wykonać o wiele lepszą pracę, niż wykonują funkcje parseInt() oraz parseFloat(). W rozdziale 11. wyjaśniono, jak używać wyrażeń regularnych.
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
185
Obliczenia arytmetyczne ActionScript 3.0 obsługuje wszystkie podstawowe operacje arytmetyczne obsługiwane przez każdy kalkulator, a wyrażenia należy wpisać w programie tak, jak na papierze. AS3 pilnuje odpowiedniego porządku operacji, dlatego 1 + 2 * 3
da w wyniku 7, co jest poprawnym wynikiem, zamiast wartości 9, która byłaby wynikiem wykonywania działań od lewej do prawej. Operacje arytmetyczne oraz porządek wykonywania działań przedstawione zostały w rozdziale 2. W tabeli 7.1 podsumowano podstawowe operatory arytmetyczne w AS3. Tabela 7.1. Podstawowe operatory arytmetyczne Operator
Znaczenie
a + b
Zsumuj a oraz b.
a * b
Pomnóż a oraz b.
a - b
a minus b.
a / b
a dzielone przez b.
a % b
a modulo b (reszta z dzielenia a przez b).
-a
Wartość przeciwna a (a razy -1).
(a + b)
Wyznacz najpierw wartość wyrażenia w nawiasach.
a++
Dodaj jeden do a po obliczeniu wyrażenia.
++a
Dodaj jeden do a przed obliczeniem wyrażenia.
a--
Odejmij jeden od a po obliczeniu wyrażenia.
--a
Odejmij jeden od a przed obliczeniem wyrażenia.
Połączenie operatorów z przypisaniem wykorzysta lewą stronę wyrażenia jako pierwszy argument. Na przykład: a = a + 10;
zwięźlej można zapisać jako: a += 10;
Poza tymi prostymi operatorami dostępne są jeszcze statyczne metody klasy Math, które zebrane zostały w tabeli 7.2, a służą do przeprowadzania bardziej złożonych obliczeń arytmetycznych. Dzięki wbudowanym operatorom oraz metodom klasy Math ActionScript 3.0 zapewnia solidne podstawy do przeprowadzania różnorodnych obliczeń.
186
Część II Praca z obiektami ActionScript 3.0
Tabela 7.2. Operacje arytmetyczne dostępne poprzez klasę Math Wywołanie metody
Zwraca
Math.pow(a, b)
a podniesione do potęgi b (a )
Math.exp(a)
e podniesione do potęgi a (e )
Math.floor(a)
a zaokrąglone w dół
Math.ceil(a)
a zaokrąglone w górę
Math.round(a)
a zaokrąglone do najbliższej liczby całkowitej
Math.max(a, b, c, ...)
maksymalna wartość z a, b, c...
Math.min(a, b, c, ...)
minimalna wartość z a, b, c...
Math.sqrt(a)
pierwiastek kwadratowy z a
Math.abs(a)
wartość bezwzględna z a
Math.log(a)
logarytm (o podstawie 10) z a
Math.ln(a)
logarytm naturalny z a
b a
Obliczenia trygonometryczne W ActionScript 3.0 wbudowane zostały również funkcje trygonometryczne. Flash jest często wykorzystywany do tworzenia interaktywnych grafik, a te rzadko kiedy da się wykonać bez pomocy trygonometrii. Z tego też powodu klasa Math zawiera również funkcje przedstawione w tabeli 7.3. Tabela 7.3. Funkcje trygonometryczne w klasie Math Wywołanie metody
Zwraca
Math.sin(a)
sinus kąta o wymiarze a radianów
Math.cos(a)
cosinus kąta o wymiarze a radianów
Math.tan(a)
tangens kąta o wymiarze a radianów
Math.asin(a)
kąt w radianach, którego sinus wynosi a (arcus sinus z a)
Math.acos(a)
kąt w radianach, którego cosinus wynosi a (arcus cosinus z a)
Math.atan(a)
kąt w radianach, którego tangens wynosi a (arcus tangens z a)
Math.atan2(a, x)
kąt, który rysowany z punktu wyjścia przecina punkt (x,y) (arcus tangens z y/x)
Klasa Math dodatkowo zawiera jeszcze stałą Math.PI dla liczby π, czyli współczynnika proporcjonalności obwodu koła do jego średnicy.
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
187
Wszystkie funkcje trygonometryczne działają na radianach, jednostce kąta, w której 2 π radianów stanowi pełny obrót. Wszystkie obroty wyświetlanych obiektów mierzone są w stopniach. Dla nich pełny obrót wynosi 360 stopni. Możesz używać równania π radianów = 180 stopni do łatwego przeliczania pomiędzy nimi: valInRadians = valInDegrees / 180 * Math.PI; valinDegrees = valInRadians / Math.PI * 180;
Generowanie losowości Dołączenie do swojego programu elementów losowości jest doskonałym sposobem symulowania nieprzewidywalnego zachowania. Można wykorzystać to do animowania grafik żyjących stworzeń lub do tworzenia sztuki generatywnej i tworzyć rzeczywiście piękne efekty oparte na rozkładzie prawdopodobieństwa. Subtelne zmiany w wyglądzie programu realizowane z wykorzystaniem losowości są bardzo powszechnie stosowaną techniką, mającą na celu wywołanie organicznego, zawsze nowego wrażenia. Atrybuty stochastyczne mogą zostać zastosowane do dowolnej wymiernej właściwości w stopniu zależnym tylko od Ciebie. Widać, że chaos znajduje liczne zastosowania w programowaniu. Wygenerowanie losowości w ActionScript 3.0 jest trywialnie proste. Math.random();
Ta metoda generuje pseudolosową liczbę z przedziału od zera do 1. Potencjalnie może ona przyjąć wartość zero, jednak nigdy nie przyjmie wartości 1 (0 ≤ n < 1). W innych językach może być konieczne ustawienie wartości zarodka dla generatora liczb losowych, jednak w ActionScript 3.0 nie jest to ani możliwe, ani potrzebne. Liczby pseudolosowe są wyznaczane w serii powtarzalnych kroków wykonywanych przez komputer. Oznacza to, że nie są w rzeczywistości przypadkowe. Posiadają jednak przypadkowy rozkład i dla większości zastosowań ich losowość jest wystarczająca. Dla zastosowań kryptograficznych powinieneś rozważyć napisanie własnych algorytmów uzyskiwania liczb losowych i użyć bardziej przypadkowych zmiennych, np. informacji o ruchu myszy czy szumu tła wychwyconego przez mikrofon.
Metoda Math.random() generuje jedynie wartości z przedziału od zera do jeden. Więcej niż pewne jest to, że będziesz potrzebował wartości leżących w innych przedziałach niż ten domyślny zakres. Pamiętaj, że funkcja Math.random() nie pobiera argumentów. Musisz przeskalować liczby do pożądanego przedziału samodzielnie. Prosty wzór służący do tworzenia losowych liczb pochodzących z przedziału określonego przez dwie wartości wygląda następująco: function randomInRange(min:Number, max:Number):Number { var scale:Number = max - min; return Math.random() * scale + min; }
To, że funkcja Math.random() zwraca wartości z przedziału od zera do 1, ułatwia manipulowanie nią. W tym przypadku po prostu mnożymy ją przez rozmiar przedziału i zwiększamy o minimalną wartość.
188
Część II Praca z obiektami ActionScript 3.0
Manipulowanie wartościami dat i czasów Gdy tworzy się aplikacje w dużym stopniu związane ze światem zewnętrznym, odpowiednia obsługa czasu w programie staje się zagadnieniem ważnym, o ile nie pierwszoplanowym. Często odliczamy lata, jakie upłynęły od naszych urodzin, chcemy nie spóźnić się na samolot, planujemy imprezę na konkretny dzień i godzinę, pamiętamy o ważnych wydarzeniach z naszej przeszłości i z historii, a także zaznaczamy przemijanie czasu. Czas jest zmienną liniową. Wszyscy doświadczamy jej zawsze w tym samym stopniu. (Tak właściwie nie jest to prawda, ale pozostawimy to zagadnienie dla pozycji Fizyka. Biblia). Potrafimy reprezentować czas w postaci pojedynczej liczby, oznaczając go za pomocą ciągle zwiększającej się liczby. Mógłbym powiedzieć: „Gotowałem wodę na herbatę przez 180 sekund”, a Ty zrozumiałbyś ten wymiar czasu. „Zrozumiałby” to również każdy komputer oraz kalkulator kieszonkowy, ponieważ liczba to tylko liczba. Sprawa zaczyna się komplikować, gdy spróbujemy wykorzystywać daty i manipulować nimi. Używamy złożonego i naprawdę pokręconego systemu do dzielenia czasu na cykle pokrywające się z wędrówką Ziemi wokół Słońca, jej obrotem, położeniem Księżyca, porami roku i wszelkiego rodzaju wydarzeniami. Ten bałagan nosi nazwę kalendarza gregoriańskiego. Dodatkowo używamy kąta padania promieni słonecznych do normalizacji naszych pomiarów i w efekcie ten sam czas reprezentowany jest na świecie jako różne godziny: to strefy czasowe. Staliśmy się niemal ekspertami w pamięciowym manipulowaniu tym wszystkim. Mógłbym Ci powiedzieć, jaki miesiąc był 50 dni temu, ale bez chwili zastanowienia nie byłbym w stanie określić, jaki to był dzień tygodnia. Zarówno dla ludzi, jak i dla komputerów obliczanie dat wymaga wiedzy na temat sposobu działania naszego systemu pomiarowego. ActionScript 3.0 uwzględnia wszystkie te reguły i pozwala Ci manipulować czasami i datami w ich powszechnie stosowanych jednostkach, a nie tylko w postaci liczb. W ActionScript 3.0 występuje jedna klasa, która reprezentuje unikalny moment w czasie, obejmująca informacje o dacie i czasie. Jest to klasa Date.
Tworzenie daty Zacznijmy korzystać z obiektów typu Date od utworzenia obiektu reprezentującego „teraz”, lub bardziej dokładnie: „chwilę, w której obiekt typu Date został utworzony”. Wystarczy do tego standardowe użycie konstruktora klasy Date. var now:Date = new Date();
Konstruktor Date jest jednym z bardziej wszechstronnych w ActionScript 3.0. Za jego pomocą może konstruować nie tylko obiekty typu Date dla bieżącej chwili, ale obiekty Date reprezentujące dowolny punkt w czasie. Możesz przekazać do konstruktora Date serię wartości będących rokiem, miesiącem, dniem miesiąca, godziną, minutą, sekundą oraz milisekundą. Obowiązkowe jest dołączenie przynajmniej dwóch parametrów — roku i miesiąca — a wszystkie pozostałe domyślnie przyjmują wartość zero (północ pierwszego dnia miesiąca).
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
189
var bday:Date = new Date(1981, 4, 12); // Tue May 12 00:00:00 GMT+0200 1981 var timeOfWriting:Date = new Date(2007, 0, 28, 14, 36, 42, 16); // Sun Jan 28 14:36:42 // GMT+0100 2007
Pewnie zauważyłeś, że coś tu się nie zgadza w datach wprowadzonych w poprzednim przykładzie. Niektóre jednostki w klasie Date są indeksowane od zera, podczas gdy inne rozpoczynają indeksowanie od jedynki. Styczeń jest pierwszym miesiącem, a klasa Date odwołuje się do niego jako zerowego. Właściwości indeksowane są od zera, gdy odwołują się do nazwanych jednostek, jak miesiące (January, February itd.) lub dni tygodnia (Monday, Tuesday itd.). W ten sposób możesz użyć tablicy z poprawnymi nazwami do bezpośredniego tłumaczenia: var daysOfWeek:Array = ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota"]; trace(daysOfWeek[timeOfWriting.day]);
Użycie liczb dla tych właściwości czyni je abstrakcyjnymi, dzięki czemu możesz łatwo wykorzystać tę technikę w celu użycia nazw w dowolnym języku. Miesiące i dni tygodnia są indeksowane od zera. Dni miesiąca są indeksowane od jedynki. Godziny, minuty, sekundy i milisekundy są mierzone od zera. Lata są liczone od zera (0 n.e.).
Trzeci sposób tworzenia obiektu typu Date polega na przekazaniu do niego łańcucha znaków reprezentującego czas. Łańcuch ten musi zawierać przynajmniej rok, miesiąc i dzień miesiąca, ale może też zawierać więcej informacji. Jeżeli konstruktor Date nie będzie mógł uzyskać daty z Twojego łańcucha znaków, utworzy nowy obiekt typu Date z wartością Invalid Date, odpowiednikiem NaN. Poniższe wiersze tworzą poprawne daty: Możliwość parsowania obiektów typu Date z łańcuchów tekstowych jest nową, wygodną funkcją ActionScript 3.0. new new new new
Date("1/28/2007"); // format daty używany w Stanach Zjednoczonych MM/DD/YYYY Date("Sun Jan 28 2007"); Date("28 Jan 2007"); Date("Sun Jan 28 2007 3:16:12 PM");
Funkcja parsująca jest wyrozumiała, ale tylko w ograniczonym stopniu, i lepiej radzi sobie z interpretacją komputerowo sformatowanych danych niż z luźnymi danym podawanymi przez człowieka. Zawsze warto zadbać o sprawdzenie poprawności daty po utworzeniu jej z łańcucha znaków. Możesz dokonać tego poprzez sprawdzenie, czy czas przez nią reprezentowany można przedstawić za pomocą liczby (więcej na ten temat napiszę w kolejnym punkcie). var invalid:Date = new Date("Wczoraj"); trace(invalid); // Invalid Date trace(isNaN(invalid.valueOf())); // true
Występuje jeszcze jedna forma konstruktora Date, używająca czasu epoki, przejdźmy zatem do niej.
190
Część II Praca z obiektami ActionScript 3.0
Czas epoki W prostszych czasach ludzie nie posiadali klas Date czy wymyślnych laptopów. Posiadali całą masę pamięci i pewne proste typy, które przechowywały wartości. Gdy potrzebowali wartości czasu — a ludzie od zawsze potrzebowali wartości czasu — polegali na swoim starym przyjacielu, typie int. Pamiętasz tę starą bajkę mówiącą, że czas jest tylko liczbą? Cóż, w dalszym ciągu może nią być, jeśli tylko będziesz mierzyć czas, począwszy od jakiegoś wydarzenia. Tamci ludzie robili to właśnie w ten sposób. Zapisywali czas jako liczbę milisekund, jakie upłynęły, począwszy od innego czasu, a liczbę tę umieszczali w zmiennej typu int. Od kiedy dokonywano tych pomiarów? Od północy 1 stycznia 1970 roku. Dlaczego jednak ten dzień? W ten sposób zawsze przedstawiano czas w systemie Unix, a dzień ten był dniem narodzin Uniksa albo przynajmniej tak twierdzą jego twórcy. W rzeczywistości podany sposób odwoływania się do czasu często nazywany jest czasem uniksowym, jednak ze względu na powszechne zastosowanie tego określenia zachowujemy podejście dyplomatyczne i nazywamy go tutaj „czasem epoki”. Oczywiście poprzez umieszczenie daty odpowiadającej czasowi epoki w liczbie ze znakiem typu int stworzyli pewien problem. Domyślasz się jaki? 32-bitowa liczba całkowita ze znakiem może przedstawiać czas tylko do wartości 231 milisekund przed lub po czasie epoki. Oznacza to, że daty sprzed 1901 roku i po 2038 roku są poza zasięgiem czasu epoki, jeżeli przechowywane są w zmiennej typu int. We wspomnianych prostszych czasach rok 2038 musiał wydawać się odległą rzeczywistością, jednak dla nas może stanowić to prawdziwy problem. W dzisiejszych czasach czas epoki nadal jest bardzo przydatnym narzędziem. Dzięki temu, że jest zwykłą liczbą, możemy manipulować nim dowolnie, używając zamiast nieporęcznych metod klasy Date prostej arytmetyki. Chcąc móc selektywnie używać czasu epoki, musimy być w stanie konwertować obiekty typu Date na czas epoki i z powrotem, a metody opisane w podrozdziale „Uzyskiwanie dostępu do daty i jej modyfikacja” pokażą, jak tego dokonywać. Możesz nie tylko użyć opisanych tam metod, nadających się jeszcze do innych sposobów modyfikacji obiektu Date, ale też możesz przekazać czas epoki do konstruktora klasy Date w celu utworzenia nowej instancji typu Date z czasu epoki. var d:Date = new Date(0); trace(d); // Thu Jan 1 01:00:00 GMT+0100 1970
Jeśli nie chcesz robić niczego z obiektami typu Date, a chcesz jedynie użyć czasu epoki, możesz sparsować łańcuchy znaków do dat bez tworzenia nowego obiektu typu Date, wykorzystując w tym celu statyczną metodę klasy Date — parse(). trace(Date.parse("1/1/1970")); // -3600000
Czy zauważyłeś coś dziwnego w tych przykładach? Czy pierwszy nie powinien zwrócić godziny 0:00 1 stycznia 1970, a drugi zera? Stałoby się tak, gdybyś żył na południku Greenwich.
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
191
Strefy czasowe Jak zapewne wiesz, jedną z komplikacji w naszym systemie pomiaru czasu jest podział naszego globu na strefy czasowe, przez co w tej samej chwili mamy różne czasy na różnych długościach geograficznych. Klasa Date uwzględnia również i to. Gdy tworzysz nowy obiekt typu Date, ActionScript zakłada, że chcesz go utworzyć w swojej strefie lokalnej, chyba że określisz strefę czasową, np. new Date("Jan 28 19:46:00 GMT+0100 2007"). Kiedy już posiadasz obiekt typu Date, możesz operować na nim i odwoływać się do niego poprzez swoją lokalną strefę czasową lub UTC. UTC lub Coordinated Universal Time (uniwersalny czas koordynowany) jest tym samym co GMT, Greenwich Mean Time (czas uniwersalny). Jest to „neutralna” strefa czasowa. Jeśli chcesz, aby dwóch ludzi w różnych częściach kuli ziemskiej zgodnie nazywało dany czas, obaj mogą odwoływać się do niego poprzez tę samą strefę czasową. Ta strefa czasowa to UTC lub GMT, strefa czasowa nad południkiem Greenwich w Wielkiej Brytanii. Odwołując się do czasów z uwzględnieniem UTC, możesz uniknąć komplikacji geograficznych. Mając na uwadze to, że czas epoki mierzony jest względem danej chwili, ten punkt odniesienia musi być stały. Czas ten to nie po prostu północ 1 stycznia 1970 roku, ale północ 1 stycznia 1970 czasu UTC. Z tego też powodu, jeśli w Polsce używasz czasu środkowoeuropejskiego, GMT+0100, lokalny czas epoki jest zatem o jedną godzinę na minusie. W pierwszym przykładzie tworzymy nowy obiekt typu Date w rzeczywistym czasie epoki, ale wypisujemy go w lokalnej strefie czasowej. Dla mnie to 1 stycznia 1970 o godzinie 1 w nocy. Gdy w UTC jest północ, w Polsce jest godzina 1 w nocy. W drugim przykładzie tworzymy nowy obiekt typu Date, żeby przedstawiał 1 stycznia w naszym czasie lokalnym. Czas epoki jest mierzony od 1 stycznia 1970 roku UTC, zatem różnica wynosi 1 godzinę lub 1000ms/s×60s/min×60min/godz.×1 godz. = 3 600 000 ms.
Uzyskiwanie dostępu do daty i jej modyfikacja Gdy już masz datę zapisaną w zmiennej typu Date, możesz pracować z nią w naturalnych jednostkach czasu dzięki metodom i funkcjom dostępowym klasy Date. Możesz ustawiać lub odczytywać dowolną jednostkę czasu w UTC lub lokalnej strefie czasowej. To, czy do odczytu i zapisu tych wartości użyjesz jawnych czy niejawnych metod dostępowych, zależy wyłącznie od Ciebie. ActionScript 3.0 rozszerza obiekt Date o niejawne metody ustawiające i pobierające dla wszystkich jednostek czasu, które już są obsługiwane przez jawne metody ustawiające i pobierające.
Metody dostępowe dla poszczególnych właściwości daty i czasu zawartych w tabeli 7.4 mają następującą postać: get[Właściwość]() do odczytania właściwości z obiektu typu Date w czasie lokalnym; getUTC[Właściwość]()
do odczytania właściwości z obiektu typu Date w UTC;
192
Część II Praca z obiektami ActionScript 3.0
Tabela 7.4. Pobieranie i modyfikowanie jednostek czasu Właściwość
Zastosowanie
Ograniczenia
milliseconds
Tysięczne sekundy (0 – 999)
seconds
Sekundy od pełnej minuty (0 – 59)
minutes
Minuty od pełnej godziny (0 – 59)
hours
Godziny dnia (0 – 23)
date
Dzień miesiąca (1 – 31)
day
Dzień tygodnia (0 – 6)
month
Miesiące (0 – 11)
fullYear
Nieskrócony rok (1999 zamiast 99)
time
Milisekundy od 1 stycznia 1970 UTC
set[Właściwość]()
Brak wersji UTC metod dostępowych (ta właściwość jest podawana zawsze względem UTC)
do ustawienia właściwości w czasie lokalnym;
setUTC[Właściwość]() [właściwość]
Tylko do odczytu (brak metod setDay() lub setUTCDay(), a niejawne metody dostępowe służą tylko do odczytu)
do ustawienia właściwości w UTC;
do odczytania i (lub) zapisania właściwości w czasie lokalnym;
[właściwość]UTC
do odczytania i (lub) zapisania właściwości w czasie UTC.
Wykorzystując powyższe metody dostępowe, możesz np. uzyskać dostęp do godzin daty: date.hours; date.hoursUTC; date.hoursUTC = 20; date.getHours(); date.setHours(5); date.getUTCHours(); date.setUTCHours(20);
Arytmetyka dat Aby zmienić czas, przekonwertuj obiekt typu Date na czas liczony względem czasu epoki i użyj zwykłej arytmetyki do manipulowania nim w milisekundach. Po zakończeniu tych manipulacji możesz zapisać nową wartość do istniejącego obiektu typu Date za pomocą metody setTime(). Takie podejście jest bardziej zalecane niż bezpośrednie ustawianie jednostek daty, jak tutaj: date.setDate(date.getDate() + 1); // dodaj jeden dzień (?)
gdyż obciąża Cię odpowiedzialnością za sprawdzanie wszelkiego rodzaju ograniczeń oraz warunków, takich jak lata przestępne czy różna liczba dni w miesiącu. Ten naiwny
Rozdział 7. Praca z liczbami i funkcjami matematycznymi
193
przykład wygląda tak, jakby za chwilę miał spowodować błąd przy próbie przypisania wartości 32 jako dnia miesiąca. Wygląda to jak pluskwa w kodzie i uznawane jest za zły styl, choć prawdopodobnie ActionScript 3.0 ochroni Cię tutaj i zrobi to, co trzeba. Wyjątkiem od tej reguły jest sytuacja, w której ilość czasu, jaki ma być dodany, jest mierzona w jednostce zależnej od specjalnych reguł gregoriańskich. Możesz np. chcieć dodać lata poprzez zwiększenie właściwości fullYear zamiast przez dodawanie do obiektu typu Date 365 dni. Użycie wartości 365 jako liczby dni w roku nie oferuje takiej dokładności, jak metoda ustawiająca fullYear, ponieważ nie uwzględnia lat przestępnych.
Czas wykonywania Funkcja flash.utils.getTimer() zwraca liczbę milisekund, jakie upłynęły od momentu inicjalizacji programu Flash Player. Gdy nie jest konieczne użycie obiektu typu Date, możesz użyć getTimer(). Funkcja ta używana jest zazwyczaj do pomiaru czasu upływającego podczas działania programu. Metoda getTimer() została w ActionScript 3.0 przeniesiona do pakietu flash.utils, dlatego musisz zaimportować ją przed jej użyciem. Klasa Timer oraz metoda getTimer() zostaną bliżej opisane w rozdziale 18.
Metoda ta może być zastosowana do pomiaru wydajności systemu lub Twojej aplikacji czy nawet sieci, a także do pomiar czasu interakcji z użytkownikiem. Wywołaj metodę getTimer() raz przed zdarzeniem, którego czas trwania chcesz zmierzyć, oraz drugi raz po jego zakończeniu, a następnie oblicz różnicę, odpowiadającą czasowi trwania tego zdarzenia.
Formatowanie daty Jesteśmy przyzwyczajeni do oglądania dat zapisywanych na przeróżne sposoby. Generując tekst zawierający daty, chcemy kontrolować wygląd tych dat, tak aby informacje o dacie były przedstawiane w najbardziej odpowiedni sposób. Flex 3.0 posiada klasę (znacznik) DateFormatter, która umożliwia wygodne formatowanie dat za pomocą łańcucha formatującego.
Możesz formatować daty w jednym wywołaniu na kilka sposobów. Obiekty typu Date posiadają jak każdy obiekt metodę toString(), jak również kilka dodatkowych metod formatujących, które przedstawiono w tabeli 7.5. Do szybkiego wyświetlenia daty metody te nadają się bardzo dobrze, jednak całkowitą kontrolę nad formatem swoich dat możesz osiągnąć poprzez wykonanie dodatkowej pracy. Polega ona na dołączaniu do daty jednostek, do których dostęp możesz uzyskać przez sam obiekt typu Date, oraz dowolnego, potrzebnego formatowania. Więcej informacji na temat formatowania łańcuchów znaków zamieszczonych zostało w rozdziale 6.
Poniższy przykład wykorzystuje własne formatowanie do wyświetlania daty w formacie MM/DD/RRRR:
194
Część II Praca z obiektami ActionScript 3.0
Tabela 7.5. Metody formatujące datę Metoda
Zawiera
Przykład
toDateString()
Miesiąc, dzień i rok
Sun Jan 28 2007
toDateLocaleString()
To samo co toDateString()
Sun Jan 28 2007
toString()
Wszystkie informacje, 24h
Sun Jan 28 2007 14:00:00 GMT+0100 2007
toLocaleString()
Wszystkie informacje poza strefą czasową 12 h
Sun Jan 28 2007 2:00:00 PM
toTimeString()
Godziny, minuty, sekundy, strefa czasowa, 24 h
14:00:00 GMT+0100
toLocaleTimeString()
Godziny, minuty, sekundy, 12 h
2:00:00 PM
toUTCString()
Wszystkie informacje względem UTC, 24 h
Sun Jan 28 2007 13:00:00 2007 UTC
function dateToMMDDRRRR(aDate:Date):String { var SEPARATOR:String = "/"; var mm:String = (aDate.month + 1).toString(); if (mm.length < 2) mm = "0" + mm; var dd:String = aDate.date.toString(); if (dd.length < 2) dd = "0" + dd; var rrrr:String = aDate.fullYear.toString(); return mm + SEPARATOR + dd + SEPARATOR + rrrr; }
Przykład ten uzupełnia miesiąc i dzień wiodącymi zerami tam, gdzie jest to konieczne, i zwiększa o jeden pole miesiąca, które — jak wiadomo — jest indeksowane od zera.
Podsumowanie
W ActionScript 3.0 występują trzy typy liczbowe, a wszystkie mają swoje mocne strony.
Wartość NaN reprezentuje błędne lub nieprzypisane wartości. Liczba typu Number nie może być ani undefined, ani void.
Przypadki brzegowe obsługiwane są przez specjalne stałe klas liczbowych.
Możesz wykonywać działania na liczbach za pomocą dostępnych operatorów.
Liczby mogą być wpisywane bezpośrednio w kodzie w wielu notacjach.
Liczby mogą być konwertowane między typami liczbowymi, parsowane z łańcuchów znaków i wyprowadzane jako łańcuchy znaków.
Klasa Math zawiera statyczne metody arytmetyczne i trygonometryczne.
Math.random()
generuje pseudolosowe liczby z przedziału do 0 do 1.
Klasa Date przechowuje informacje o dacie i czasie.
Czas epoki przechowuje informacje o dacie i czasie w postaci liczby.
Rozdział 8.
Używanie tablic W tym rozdziale:
Praca z tablicami indeksowanymi, tablicami asocjacyjnymi i słownikami
Dodawanie zawartości do tablicy
Używanie tablic jako kolejek i stosów
Szukanie wartości w tablicy
Sortowanie elementów tablicy
Wykonywanie operacji na wszystkich elementach tablicy
W tym rozdziale przyjrzymy się tablicom. Tablice są typem uporządkowanego zbioru danych i są podobne do numerowanej listy. W tablicy możesz przechować dowolną liczbę pojedynczych elementów danych w jednej zmiennej, co umożliwia grupowanie wartości, które powinny być przechowywane razem. Klasa Array oferuje również metody i właściwości umożliwiające pracę z tym zestawem danych poprzez ich edycję, przeszukiwanie, sortowanie i operowanie na całej grupie wartości. Tablice są wykorzystywane bardzo często w prawie wszystkich językach programowania, ActionScript nie jest wyjątkiem. Tablice są pierwszym złożonym typem danych, jaki omawiamy w części II tej książki. Łańcuchy znaków, liczby i wartości boolowskie są prostymi typami danych — są podstawowymi elementami informacji i zazwyczaj zawierają pojedynczy fragment niezmiennych danych konkretnego typu. Złożone typy danych są kompozycjami składającymi się z różnych prostych typów danych.
Podstawy tablic Jak już pisaliśmy wyżej, tablice bardzo przypominają numerowaną listę elementów. Każdy element w tablicy posiada pozycję, inaczej indeks. Indeksy w tablicy rozpoczynają się od 0, a nie od 1 jak w większości numerowanych list. Indeksy te są używane do lokalizowania elementów w tablicy, co przedstawione zostało na rysunku 8.1.
196
Część II Praca z obiektami ActionScript 3.0
Rysunek 8.1. Chcąc wyobrazić sobie tablicę, pomyśl o zestawie przegródek, w których znajdują się pojedyncze elementy
Konstruktor Array Utwórzmy naszą pierwszą tablicę i wypełnijmy ją danymi. W tym celu użyjesz konstruktora klasy Array. var myArray:Array = new Array();
Pamiętaj o tym, że konstruktor jest funkcją tworzącą instancję klasy. Nazwa konstruktora zawsze wywodzi się od nazwy klasy, w której on istnieje. Aby użyć konstruktora, użyj słowa kluczowego new, a po nim zapisz nazwę konstruktora — np. new Array().
Przedstawiony powyżej kod tworzy pustą tablicę — tablicę bez zawartości. Zwykłe wywołanie metody Array() bez argumentów jest najprostszą formą konstruktora tablicy. ActionScript oferuje jednak jeszcze dwa dodatkowe sposoby używania tego konstruktora. Jeśli chcesz wywołać konstruktor i przypisać od razu wartości, po prostu przekaż do konstruktora w postaci parametrów wartości, jakie chcesz wstawić do tablicy: var myThings:Array = new Arrray("filtry do kawy", "spinacz", "płyta Spin Doctors");
W tym przypadku tworzysz tablicę ze swoimi rzeczami i jednocześnie wypełniasz ją jakimiś rzeczami znalezionymi w jednej z szuflad swojego biurka. Tutaj wypełniamy tablicę łańcuchami znaków, ale tablice w ActionScript mogą zawierać dowolny typ danych: var time:Date = new Date(); var currentTemp:Number = 23; var currentConditions:String = "Lekkie opady"; var weatherReport:Array = new Array(time, currentTemp, currentConditions); trace(weatherReport); // wyświetli: Tue Sep 16 15:03:13 GMT+0100 2008,23,Lekkie opady
W poprzednim przykładzie użyliśmy instrukcji trace() do wyświetlenia zawartości tablicy. Jest to zdecydowanie najprostszy sposób sprawdzenia wartości przechowywanych w tablicy. Instrukcja trace() wyświetla wyniki działania metody toString() wywołanej dla tablicy. Realizowane jest to poprzez wyświetlanie wyniku działania metody toString() wywoływanej dla wszystkich elementów zawartych w tablicy. Bardziej szczegółowo zostanie to opisane w podrozdziale „Konwersja tablic na łańcuchy znaków”. Trzeci i już ostatni sposób użycia konstruktora klasy Array umożliwia określenie rozmiaru nowej tablicy. Wywołaj konstruktor Array i przekaż do niego pojedynczą wartość całkowitą określającą rozmiar tablicy. Utworzona zostanie nowa tablica z określoną przez Ciebie liczbą nieprzypisanych indeksów: var topTen:Array = new Array(10); // utworzy tablicę z dziesięcioma komórkami
Rozdział 8. Używanie tablic
197
W praktyce możliwość ta nie jest nadzwyczajnie przydatna. Rozmiar tablicy nie może być definiowany statycznie, jak to jest możliwe w innych językach programowania, np. w języku Java. Rozmiar tablicy zmienia się po dodaniu do niej elementów lub po ich usunięciu. Taki rodzaj tablicy czasami nazywa się wektorem. Tablice w ActionScript będą pozwalały Ci na wstawienie dodatkowych elementów poza określonym rozmiarem. Z tego też powodu definiowanie rozmiaru tablicy jest kwestią sporną, gdyż jest to wartość mogąca ulec zmianie. Jeżeli przekażesz do konstruktora tablicy liczbę z zamiarem dodania tej liczby do tablicy, rozczarujesz się. Pojedyncza liczba zawsze spowoduje utworzenie pustej tablicy z pustymi komórkami. Zamiast tego możesz użyć literału tablicowego lub dodać wartości po wywołaniu konstruktora.
Tworzenie tablicy za pomocą literału tablicowego ActionScript oferuje bardzo przydatny, skrócony sposób tworzenia nowych tablic, nazywany literałem tablicowym. Jak już pisałem w rozdziale 2., literał to fragment danych, który może zostać wprowadzony bezpośrednio w kodzie bez używania zmiennych, konstruktorów czy innego typu struktur obiektowych. Literały tablicowe tworzy się podobnie jak literały liczbowe i literały łańcuchów znaków. Aby utworzyć literał tablicowy, umieść wszystkie wartości, które chcesz zapisać w tablicy, pomiędzy parą kwadratowych nawiasów ([]), rozdzielając je przecinkami. Na przykład: [1,2,3]
Powyższy kod odpowiada: new Array(1, 2, 3);
Jak widzisz, pierwszy sposób jest znacznie prostszy w zapisie. Oczywiście samo utworzenie tablicy nie jest za bardzo przydatne, dopóki nie zaplanujesz, co chcesz z nią zrobić. Przypiszmy wartość do zmiennej za pomocą literału tablicowego: var fibonacci:Array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
Przy użyciu pustego literału tablicowego możesz również utworzyć nową, pustą tablicę: var a:Array = [];
Literały są szybkim i wygodnym sposobem tworzenia tablic.
Odwoływanie się do wartości w tablicy Utworzyliśmy kilka tablic na przeróżne sposoby. Teraz przyjrzymy się sposobowi uzyskiwania dostępu do wartości zapisanych w tablicy. Jak już wcześniej wspominaliśmy, wartości w tablicy zapisywane są w komórkach zwanych indeksami. Indeksy są numerowane, począwszy od 0 i licząc aż do wartości o jeden mniejszej od rozmiaru tablicy. Aby podejrzeć wartość danej komórki, zapisujemy nazwę tablicy, a po niej operator dostępu do tablicy ([]) zawierający indeks danej komórki. Nie pomyl tego z literałem tablicy, który używany jest samodzielnie. Operator dostępu do tablicy każe tablicy zwrócić konkretny element przypisany podanemu przez Ciebie indeksowi:
198
Część II Praca z obiektami ActionScript 3.0 var animals:Array = ["jaszczurka", "chomik", "kobra", "żółw"]; trace(animals[2]); // wyświetli: kobra
Pisząc animals[2], żądamy elementu zapisanego w tablicy pod indeksem 2. Uzyskiwanie dostępu do elementu za pomocą notacji tablicowej jest podobne do uzyskiwania dostępu do zmiennej bez nazwy. Notacja ta może służyć również do zapisywania wartości w tablicy. Po prostu umieść tablicę z numerem indeksu z lewej strony instrukcji przypisania. animals[0] = "salamandra"; trace(animals); // wyświetli: salamandra, chomik, kobra, żółw
Pobieranie liczby elementów w tablicy Aby dowiedzieć się, ile elementów znajduje się w tablicy, możesz sprawdzić właściwość length tablicy, co pokazane zostało w poniższym kodzie. length jest właściwością tylko do odczytu. Nie możesz ustawić rozmiaru tablicy bez dodania do niej nowych wartości lub określenia rozmiaru tablicy w konstruktorze tablicy. var veggies:Array = new Array(); trace(veggies.length); // wyświetli: 0 veggies = ["kukurydza", "por", "brokuł", "burak"]; trace(veggies.length); // wyświetli: 4
Pamiętaj o tym, że indeksy zaczynają się od 0, zatem rozmiar tablicy jest zawsze o jeden większy od ostatniego indeksu w tablicy. Innymi słowy, veggies[3] jest ostatnim elementem tablicy, a veggies[4] jest niezdefiniowane.
Konwersja tablic na łańcuchy znaków Podczas pracy z tablicami często przydatna jest możliwość utworzenia migawki z jej zawartości. Zapewne znasz już metodę toString(). Jest ona niejawnie wywoływana przez funkcje takie jak trace(), które umożliwiają szybkie sprawdzenie zawartości. Standardowo tablice wyświetlają listę wartości rozdzielonych przecinkami. var names:Array = ["Joanna", "Józef", "Jacek"]; trace(names); // wyświetli: Joanna,Józef,Jacek
Klasa Array oferuje alternatywny sposób wyświetlania swoich wartości w postaci łańcucha znaków. Poprzez użycie metody join() możesz określić swój własny separator rozdzielający zawartości tablicy. Możesz użyć join() z wybranym przez siebie tekstowym separatorem w miejscu, gdzie użyłbyś toString(): var menu:Array = ["schabowy", "ziemniaki", "surówka"]; trace("Dzisiejszy zestaw obiadowy to: " + menu.join(" ") + ". Mniam!"); // wyświetli: Dzisiejszy zestaw obiadowy to: schabowy, ziemniaki, surówka. Mniam!
Po przecinkach dodana została spacja, co uczyniło zdanie czytelnym i bardziej naturalnym. Aby utworzyć łańcuch znakowy bez odstępów lub innych znaków pomiędzy wartościami tablicy, użyj pustego łańcucha: join("").
Rozdział 8. Używanie tablic
199
Dodawanie i usuwanie elementów z tablicy Dodawanie elementów do tablicy dla pojedynczych indeksów jest dobre, ale w miarę zwiększania się liczb okiełznanie wszystkich tych indeksów może stać się kłopotliwe. Na szczęście nie będziesz musiał tego robić. Klasa Array dostarcza metody służące do dodawania i usuwania elementów z tablicy, bez odwoływania się do numerów indeksów. Taka funkcjonalność umożliwia proste rozszerzanie zawartości tablicy lub pracę przypominającą pracę ze stosem lub kolejką. Wkrótce opiszemy ich działanie, ale zacznijmy od dodania do siebie dwóch lub więcej tablic poprzez zastosowanie konkatenacji.
Dołączenie wartości na końcu tablicy za pomocą concat() Poznałeś już sposób tworzenia pojedynczych tablic, a teraz pokażemy, jak można łączyć ze sobą dwie tablice. Pierwszą możliwością, jaką wypróbujesz, będzie dodanie do siebie tych dwóch tablic: var a:Array = [1,2,3]; var b:Array = [4,5,6]; var c:Array = a + b;
Wykonanie tego kodu spowoduje wystąpienie następującego komunikatu o błędzie: TypeError: Error #1034: Type Coercion failed: cannot convert "1,2,34,5,6" to Array.
O co tutaj chodzi? Obie tablice są złożonymi typami danych i dlatego nie są odpowiednimi wartościami dla zastosowania operatora dodawania. Zamiast tego są automatycznie konwertowane na łańcuchy znaków, które są do siebie dodawane, dając w rezultacie 1,2,34,5,6. Błąd ten wystąpił dlatego, że próbujemy przypisać wartość łańcuchową do zmiennej c, która „spodziewa się” tablicy. Błąd Type Coercion oznacza zazwyczaj wystąpienie próby przypisania wartości do zmiennej dostosowanej do innego typu — np. próby przypisania obiektu typu Date do zmiennej typu Array.
Zamiast dodawać te dwie tablice za pomocą symbolu plus, możesz użyć metody concat() przeprowadzającej konkatenację. Metoda ta tworzy nową tablicę w oparciu o tablicę wywołującą tę metodę i dodaje na końcu nowej tablicy obiekty w liczbie określonej poprzez parametry tej metody. Jeśli przekażesz do metody jako parametr tablicę, to dodana zostanie jej zawartość, a nie sam obiekt tablicy. Poniższy przykład dodaje a do b i dla rozrywki dorzuca jeszcze kilka dodatkowych liczb: var a:Array = [1,2,3]; var b:Array = [4,5,6]; var c:Array = a.concat(b, 7, "osiem"); trace(a); // wyświetli: 1,2,3 trace(b); // wyświetli: 4,5,6 trace(c); // wyświetli: 1,2,3,4,5,6,7,osiem
Zauważ, że oryginalne tablice a oraz b pozostają podczas tej operacji bez zmian. Metoda concat() zwraca zupełnie nową tablicę opartą na oryginale, ale nie zmienia samego oryginału.
200
Część II Praca z obiektami ActionScript 3.0
Stosowanie operacji stosu — push() oraz pop() ActionScript oferuje dla tablic interfejsy zezwalające im na działanie podobne do działania jednej z wielu różnych struktur danych lub dobrze zorganizowanych zbiorów danych. Jedna z takich struktur danych nazywana jest stosem. Stos jest listą danych zorganizowanych zgodnie z zasadą: pierwsze wchodzi, ostatnie wychodzi, którą określa się skrótem FILO (ang. first in/last out). Oznacza to, że pierwszy fragment danych dodany do stosu pozostanie na miejscu tak długo, aż usunięte zostaną wszystkie pozostałe fragmenty. Być może łatwiej będzie myśleć o stosie jak o dozowniku cukierków Pez. Słodycze ładowane od góry zostają spychane w dół stosu. Gdy podniesiesz główkę dozownika, ostatnio dodany łakoć wyskakuje jako pierwszy. Klasa Array dostarcza metody robiące dokładnie to samo dla elementów tablicy. Przedstawiam Ci push() oraz pop(). Podobnie jak w przypadku dozowników Pez, tak i tu wartości mogą być umieszczane na końcu tablicy za pomocą metody push() i pobierane z końca za pomocą metody pop(). W przeciwieństwie do większości innych metod tablicowych, z którymi się spotkałeś wcześniej, te dwie, zamiast tworzyć nową tablicę, działają bezpośrednio na tablicy wywołującej metodę. Przyjrzymy się ich działaniu: var pez:Array = new Array(); pez.push("wiśnia"); pez.push("pomarańcza"); pez.push("cytryna"); pez.push("winogrono"); trace(pez); // wyświetli: wiśnia,pomarańcza,cytryna,winogrono var candy:String = String(pez.pop()); // rzutujemy jako String, aby mieć pewność, że to jest łańcuch znaków, ponieważ elementy tablicy // nie mają początkowo określonego typu trace(candy); // wyświetli: winogrono trace(pez); // wyświetli: wiśnia,pomarańcza,cytryna
Stos przydaje się wtedy, gdy Twój program musi zajmować się nowymi, przychodzącymi danymi, a jednocześnie musi zachowywać starsze dane w celu odwołania się do nich później. Wyobraź sobie system alarmowy wyświetlający bieżący poziom zagrożenia w postaci odpowiedniego koloru. Gdy poziom zagrożenia wzrasta, kolor może zmienić się z zielonego na żółty, a po przeminięciu zagrożenia może znów być zielony. Metoda push() pobiera jako parametry jeden lub więcej obiektów i dodaje je wszystkie po kolei na końcu tablicy. Zostają dodane do ostatniego indeksu tablicy, co czyni te dwie instrukcje identycznymi: myArray.push(myValue); myArray[myArray.length] = myValue;
Niezależnie od tego, czy zdecydujesz się na użycie formalnej struktury stosu, metoda push() jest bardzo szybkim i przydatnym sposobem dodawania elementów na końcu tablicy. Nie wymaga od Ciebie znajomości numeru indeksu, do którego element zostanie przypisany. Metoda pop() nie pobiera żadnych argumentów. Usuwa i zwraca ostatni element tablicy. Nie ma odpowiednika dla metody pop(), gdyż element tablicy nie może zostać całkowicie usunięty przez operator dostępu do tablicy ([]).
Rozdział 8. Używanie tablic
201
Stosowanie operacji kolejki — shift() oraz unshift() Innym, podobnym do stosu typem struktury danych jest kolejka. Kolejka jest listą pracującą zgodnie z zasadą: pierwsze wchodzi, pierwsze wychodzi (first in/first out), określaną również skrótem FIFO. Można to porównać do szeregu ludzi (lub kolejki) czekających na bilet w kinie. Pierwsza osoba w kolejce jako pierwsza otrzyma bilet. Kolejna osoba z kolejki jest następną osobą kupującą bilet itd. Zawsze gdy osoba z kolejki kupi bilet, pozostała części kolejki przesuwa się do przodu o jedno miejsce. Tablice również potrafią „kłaść” elementy, a potem je „zdejmować” za pomocą metody shift(). Utwórzmy teraz kolejkę w kinie wykorzystującą tę technikę. var queue:Array = new Array(); queue.push("Ania"); queue.push("Janek"); queue.push("Wojtek"); trace(queue); // wyświetli: Ania,Janek,Wojtek var person:String = String(queue.shift()); trace(person); // wyświetli: Ania trace(queue); // wyświetli: Janek,Wojtek
Jak widzisz, pierwsza osoba dodana do listy była pierwszą osobą z niej wyjętą. Metoda shift() zwraca wartość usuniętą z tablicy. Jeśli metoda shift() służy do zabierania elementów z początku tablicy, to do dodawania elementów na początku tablicy służy metoda unshift(). Tablice mogą też wywoływać unshift() w celu dodania dowolnej liczby elementów na początku tablicy, tak jak metoda push() dodaje elementy na końcu tablicy. queue.unshift("Jerzy", "Dorota"); trace(queue); // wyświetli: Jerzy,Dorota,Janek,Wojtek
Kolejki są bardzo przydatne w sytuacjach, gdy dane muszą być przetwarzane zgodnie z regułą: pierwsze weszły, pierwsze obsłużone. Często spotykanym przykładem ich zastosowania jest wczytywanie po kolei różnych obrazów z serwera. W takich przypadkach chce się, aby wczytany został cały pierwszy obraz, zanim wczytywany będzie kolejny. W ten sposób przechodzi się do kolejnych obrazów, dopóki wszystkie nie zostaną wczytane. Operacje stosu i kolejki nie wykluczają się wzajemnie. Mogą być używane oddzielnie lub razem dla jednej tablicy.
Cięcie i łączenie W poprzednim podrozdziale przyjrzałeś się dodawaniu elementów na początku lub na końcu tablicy. Jest to bardzo przydatne, jednak mając w tablicy więcej elementów, możesz chcieć w bardziej wybiórczy sposób decydować o tym, gdzie elementy mają zostać wstawione lub skąd mają zostać usunięte. Możesz nawet chcieć pracować z mniejszym podzbiorem swojej tablicy, niezależnie od oryginalnej tablicy. Ta część rozdziału opisuje selektywną pracę z elementami tablicy.
202
Część II Praca z obiektami ActionScript 3.0
Wstawianie i usuwanie wartości za pomocą splice() Wyraz splice w języku angielskim zazwyczaj oznacza łączenie dwóch rzeczy, np. kawałków filmu lub liny, poprzez przeplatanie ich. Pracując z tablicami, możesz połączyć w całość dwa zbiory elementów za pomocą metody splice(). Łącząc w całość film, zawsze musisz zniszczyć jedną klatkę w celu uzyskania płynnego przejścia. W przypadku tablic masz możliwość usuwania elementów podczas dołączania nowych elementów. Metoda splice() pobiera dwa wymagane parametry. Indeks początkowy, wskazujący miejsce rozpoczęcia wstawiania lub usuwania, oraz licznik usuwania, oznaczający liczbę elementów przeznaczonych do usunięcia z tablicy. Jeśli nie chcesz usuwać elementów, jego wartość musi wynosić 0. Możesz też dodać kilka opcjonalnych parametrów będących elementami dodawanymi do tablicy, począwszy od indeksu początkowego. Jeśli jakieś elementy zostaną usunięte z tablicy, zostaną one zwrócone przez metodę splice(). Przyjrzyjmy się działaniu metody splice(): var nobleGasses:Array = ["hel", "neon", "argon", "pentagon", "ksenon", "radon"]; var shapes:Array = nobleGasses.splice(3, 1, "krypton"); trace(nobleGasses); // wyświetli: hel,neon,argon,krypton,ksenon,radon trace(shapes); // wyświetli: pentagon
Praca z podzbiorem tablicy przy użyciu slice() Aby wydobyć podzbiór tablicy w celu pracy z nim niezależnie od oryginału tablicy, możesz użyć metody slice(). Jeśli przeczytałeś rozdział 6., być może pamiętasz metodę slice() klasy String. Metoda ta działa bardzo podobnie dla klasy Array. Wystarczy określić indeksy początkowy i końcowy (nie będzie wliczany) wycinku, jaki chcesz utworzyć. var metals:Array = ["żelazo", "miedź", "złoto", "srebro", "platyna", "cyna", "chrom"]; var preciousMetals:Array = metals.slice(2,5); trace(preciousMetals); // wyświetli: złoto,srebro,platyna
Metoda slice() akceptuje również wartości ujemne, które powodują zliczanie od końca tablicy. var metals:Array = ["żelazo", "miedź", "złoto", "srebro", "platyna", "cyna", "chrom"]; var canMakingMetal:Array = metals.slice(-2,-1); trace(canMakingMetal); // wyświetli: cyna
Zwróć również uwagę na metody filter() oraz map() opisane w dalszej części tego rozdziału.
Rozdział 8. Używanie tablic
203
Iteracje po elementach tablicy Podczas pracy z tablicami często będziesz chciał wykonać jakąś operację z wykorzystaniem wszystkich elementów tablicy. Do tej pory przyglądaliśmy się sposobom tworzenia, dodawania i usuwania elementów z tablicy. Teraz przyjrzymy się temu, jak możesz na nich pracować.
Użycie pętli for Najbardziej powszechnym sposobem uzyskiwania dostępu do wszystkich elementów tablicy jest użycie pętli for. Z drugiej strony, jest to prawdopodobnie najpowszechniejsze zastosowanie pętli for. Pętle powtarzające opisaliśmy w rozdziale 2. Teraz możesz zastosować je w praktyce. Pracując z tablicami, często spotykać się będziesz z podobną konstrukcją wykorzystującą pętlę for: for (var i:int = 0; i < myArray.length; i++) { trace(myArray[i]); }
Przyjrzymy się temu dokładniej: for
— słowo kluczowe for określa rozpoczęcie pętli.
— utwórz nową zmienną typu całkowitego i ustaw jej początkową wartość na zero. Litera „i” pochodzi od wyrazu „iteracja” lub „indeks” i reprezentuje położenie w tablicy.
var i:int = 0
— oznacza, że pętla powinna być powtarzana, dopóki indeks będzie mniejszy od rozmiaru tablicy.
i < myArray.length
i++ — po każdej pętli indeks używa operatora inkrementacji do zwiększenia swojej
wartości o jeden. trace(myArray[i])
— poprzez użycie w operatorze tablicowym wartości
i zmieniającej się w kolejnych przebiegach pętli możesz sprawdzić wartość
każdego elementu tablicy. To nie jest jedyny sposób przechodzenia po kolejnych elementach tablicy — np. pętle while również nadają się do tego zastosowania. To po prostu sposób najbardziej popularny.
Użycie metody forEach() ActionScript 3.0 definiuje w postaci metody forEach() nową metodę dla klasy Array, stanowiącą alternatywę dla tradycyjnej pętli for. Ta metoda pomija formalności związane z użyciem pętli for i dla każdego elementu tablicy automatycznie stosuje wybraną funkcję. Funkcja ta jest przekazywana jako parametr i może być zdefiniowana w dowolnym miejscu klasy lub przekazana jako dynamiczna funkcja za pomocą wyrażenia funkcyjnego zgodnie z tym, co opisywaliśmy w rozdziale 4.
204
Część II Praca z obiektami ActionScript 3.0
Funkcja użyta dla wywołania forEach() pobiera trzy parametry. Pierwszy z nich to wartość elementu tablicy, drugi to indeks tablicy, a trzeci jest samą tablicą. Powinna odpowiadać następującej sygnaturze funkcji: function functionName(element:*, index:int, array:Array):void
W tym przykładzie po prostu prześledzimy wartości zawarte w tablicy za pomocą forEach(): var subjects:Array = ["Plastyka", "Biologia", "Matematyka", "Historia"]; function traceArray(element:*, index:int, a:Array):void { trace("[" + index + "] = " + element); } subjects.forEach(traceArray);
Dla każdego elementu tablicy wywołana zostanie funkcja traceArray(), co da następujące wyniki: [0] [1] [2] [3]
= = = =
Plastyka Biologia Matematyka Historia
Metoda forEach() pozwala na zatrzymanie i ponowne użycie kodu do iteracji po tablicach. AS3 wprowadza kilka innych wariantów tej metody do klasy Array w celu automatycznej iteracji po tablicy. Pozostałe z tych metod opiszemy dalej w tym rozdziale, w podrozdziale zatytułowanym „Przeprowadzanie operacji na wszystkich elementach tablicy”.
Wyszukiwanie elementów ActionScript 3.0 wprowadza możliwość wyszukiwania w tablicy dowolnego elementu za pomocą metod indexOf() oraz lastIndexOf(). Możesz już znać te funkcje, jeśli pracowałeś z klasą String, ponieważ metody te dla klasy Array działają dokładnie tak samo. Metoda zwróci indeks komórki zawierającej określony element. Poniższy przykład pokazuje, jak pobrać indeksy takich elementów z tablicy. Zwróć uwagę na to, że wartość „Ocelot” nie istnieje i z tego też powodu zwraca -1 jako swój indeks, co wskazuje na to, że element nie mógł zostać odnaleziony. var cats:Array = ["Gepard", "Puma", "Jaguar", "Pantera", "Tygrys", "Leopard"]; trace(cats.indexOf("Leopard")); // wyświetli: 5 trace(cats.indexOf("Ocelot")); // wyświetli: -1
Więcej informacji o metodach indexOf() w odniesieniu do łańcuchów znaków znajdziesz w rozdziale 6.
Zmiana kolejności w tablicy Tablice reprezentują uporządkowane zbiory danych, podobnie jak bazy danych. W większości przypadków kolejność elementów w tablicy oparta jest na kolejności wprowadzania do niej danych, a czasami kolejność może nie mieć dla Ciebie żadnego znaczenia. Innym
Rozdział 8. Używanie tablic
205
razem możesz chcieć uporządkować dane, np. alfabetycznie, według tego, czy są prawdziwe lub fałszywe, według liczby kalorii w utworzonym obiekcie Food, według koloru wypełnienia rysunku czy liczby punktów uzyskanych przez graczy w grze komputerowej. Na szczęście ActionScript oferuje otwarte rozwiązanie służące do sortowania wartości w tablicy w porządku najbardziej odpowiadającym danemu zastosowaniu.
Odwracanie kolejności tablicy za pomocą reverse() Jednym z najszybszych sposobów zmiany kolejności elementów w tablicy jest jej odwrócenie — pierwszy element będzie ostatnim i odwrotnie. Aby tego dokonać, użyj metody reverse() dla swojej tablicy: var myArray:Array = [12,6,17,4,39]; myArray.reverse(); trace(myArray); // wyświetli: 39,4,17,6,12
Metoda ta powoduje zmianę kolejności w tablicy, dla której zostanie wywołana, nie tworząc nowej tablicy.
Użycie funkcji sortujących Sortując tablice w ActionScript, nie musisz ograniczać się jedynie do liczb i alfabetu, możesz sortować na dowolne sposoby. Klasa Array posiada dwie metody, które to umożliwiają — sort() oraz sortOn(). Pierwsza z nich będzie sortować elementy w oparciu o dostarczony przez Ciebie algorytm. Druga używa podczas sortowania właściwości obiektów. Obie funkcje stosują alfabetyczne wyszukiwanie, bazując na wartości łańcuchowej elementów tablicy, jeśli nie zostaną do nich przekazane żadne parametry. W przypadku obu funkcji będziesz potrzebował danych do posortowania. Rozpoczniemy od własnej klasy Book zawierającej informacje o tytule, autorze oraz roku publikacji książki. package pl.helion.as3biblia { public class Book { public var title:String; public var author:String; public var year:int; public function Book (title:String, author:String, year:int) { this.title = title; this.author = author; this.year = year; } public function toString():String { return '"' + title + '", ' + author + ' (' + year + ')'; } } }
Rozpoczynając, musimy posiadać jakieś dane. Załóżmy, że utworzyliśmy już kilka obiektów książek i dodaliśmy je do tablicy, tak że instrukcja: trace(bookshelf.join("\n");
206
Część II Praca z obiektami ActionScript 3.0
spowoduje wyświetlenie: "Sztuka efektywności. Skuteczna realizacja zadań", Allen (2002) "Lot nad kukułczym gniazdem", Ken (1981) "ActionScript 3.0. Biblia", Braunstein, Wright, Lott & Noble (2008) "Hamlet", Shakespeare (1601)
Posiadając taki oto zbiór danych, możemy przystąpić do poznawania funkcji sortujących. Rozpoczniemy od metody sort(). Jak już zostało wspomniane, użycie sort() bez dodatkowych parametrów posortuje elementy alfabetycznie w oparciu o wartość tekstową obiektu. Ponieważ zdefiniowaliśmy metodę toString() dla klasy Book, służącą do wypisania tytułu książki, następnie autora i daty publikacji, powinniśmy zobaczyć listę posortowaną alfabetycznie. bookshelf.sort();
powoduje zmianę kolejności w tablicy i daje w rezultacie taką kolejność: "ActionScript 3.0. Biblia", Braunstein, Wright, Lott & Noble (2008) "Hamlet", Shakespeare (1601) "Lot nad kukułczym gniazdem", Ken (1981) "Sztuka efektywności. Skuteczna realizacja zadań", Allen (2002)
Pamiętaj o tym, że metoda sort() powoduje zmiany bezpośrednio w tablicy ją wywołującej. Spróbujmy teraz utworzyć funkcję służącą do sortowania tych danych. Funkcje, jakie będziesz pisał dla metody sort(), powinny odpowiadać następującej sygnaturze: function sortFunction(valueA:*, valueB:*):Number
gdzie valueA i valueB są dwiema dowolnymi wartościami z tablicy. Powinieneś zdefiniować funkcję tak, aby zwracała wartość numeryczną w oparciu o porównanie tych dwóch wartości. Podczas określania, jakie wyniki powinna generować Twoja funkcja, kieruj się następującymi zasadami:
Jeśli valueA powinno znaleźć się przed valueB, zwracaj -1.
Jeśli valueB powinno znaleźć się przed valueA, zwracaj 1.
Jeśli valueA i valueB są równe, zwracaj 0.
Możesz użyć tego systemu do sortowania elementów według dowolnego wybranego kryterium. Spróbujmy posortować książki według dat publikacji: function byDate(valueA:Book, valueB:Book):Number { if (valueA.year == valueB.year) {return 0;} else if (valueA.year < valueB.year) {return -1;} else {return 1;} } bookshelf.sort(byDate);
Funkcja ta porównuje właściwość year obiektu klasy Book w celu sprawdzenia, która data powinna być pierwsza, i sortuje listę od najstarszej do najnowszej: "Hamlet", Shakespeare (1601) "Lot nad kukułczym gniazdem", Ken (1981) "Sztuka efektywności. Skuteczna realizacja zadań", Allen (2002) "ActionScript 3.0. Biblia", Braunstein, Wright, Lott & Noble (2008)
Rozdział 8. Używanie tablic
207
Możesz wpływać na sposób sortowania poprzez dodanie opcjonalnych flag sortowania. Są one przechowywane w klasie Array w postaci statycznych stałych. Aby dodać te opcje, przekaż je rozdzielone operatorem „bitowego lub” (|) po Twojej funkcji sortującej (w tym przypadku będziemy sortować według daty). Jeśli nie znasz się na działaniach bitowych, nie przejmuj się. Poniższy kod posortuje elementy tablicy numerycznie (zamiast domyślnego używania tekstowego odpowiednika daty) oraz malejąco od najwyższego roku do najniższego. bookshelf.sort(byDate, Array.NUMERIC | Array.DESCENDING);
da jako wynik: "ActionScript 3.0. Biblia", Braunstein, Wright, Lott & Noble (2008) "Sztuka efektywności. Skuteczna realizacja zadań", Allen (2002) "Lot nad kukułczym gniazdem", Ken (1981) "Hamlet", Shakespeare (1601)
Istnieje pięć takich opcjonalnych flag, które możesz przekazać do metod sort() oraz sortOn(): — zwykłe sortowanie uwzględnia wielkość znaków (ang. case-sensitive). Użycie tej flagi spowoduje ignorowanie wielkości znaków podczas sortowania.
CASEINSENSITIVE
DESCENDING — użycie tej flagi spowoduje sortowanie tablicy od najwyższej wartości
do najniższej. — jeśli sortujesz wyłącznie liczby, użyj tej flagi. W przeciwnym razie przed rozpoczęciem sortowania liczby zostaną przekonwertowane na ich tekstowe odpowiedniki.
NUMERIC
— ta flaga spowoduje zwrócenie przez funkcję sortującą posortowanej tablicy bez przeprowadzania zmian w oryginalnej tablicy, dla której ta metoda została wywołana.
RETURNINDEXEDARRAY
— gdy ustawiona jest ta flaga, to w przypadku odnalezienia w tablicy dwóch takich samych elementów metoda sortująca zatrzyma swoje działanie i zwróci 0.
UNIQUESORT
Przyglądaliśmy się wykorzystaniu metody sort() do porównywania elementów za pomocą funkcji. Teraz przyjrzyjmy się metodzie sortOn(), która umożliwia automatyczne porównywanie właściwości dwóch elementów. Zamiast pobierać jako argument funkcję sortującą, metoda sortOn() pobiera tablicę z jedną lub więcej właściwościami w celu użycia jej jako kryteriów sortowania. Jeśli przekazana zostanie pojedyncza właściwość, elementy zostaną posortowane według tego kryterium. Jeśli przekazana zostanie więcej niż jedna właściwość, elementy będą sortowane najpierw według pierwszej właściwości, następnie według drugiej właściwości itd. Pominięcie pierwszego parametru spowoduje alfabetyczne sortowanie listy, tak jak w przypadku metody sort(). Przekazywane mogą być również opcjonalne parametry, a odbywa się to w ten sam sposób, co dla metody sort(). bookshelf.sortOn(["year"]);
Ten przykład sortuje według właściwości year i jego wynik jest identyczny jak dla wcześniejszego przykładu z byDate().
208
Część II Praca z obiektami ActionScript 3.0 "Hamlet", Shakespeare (1601) "Lot nad kukułczym gniazdem", Ken (1981) "Sztuka efektywności. Skuteczna realizacja zadań", Allen (2002) "ActionScript 3.0. Biblia", Braunstein, Wright, Lott & Noble (2008)
Przeprowadzanie operacji na wszystkich elementach tablicy Jak mogłeś się przekonać, czytając podrozdział „Iteracje po elementach tablicy”, metoda forEach() umożliwia automatyczną iterację po tablicy i zastosowanie funkcji dla każdego elementu. W tym podrozdziale opiszemy metody, jakie ActionScript 3.0 wprowadza w celu stosowania funkcji dla elementów tablicy. Wszystkie funkcje opisane w tym podrozdziale są nowością w ActionScript 3.0.
Wszystkie te metody pobierają jako parametry funkcje z tą samą sygnaturą, co dla metody forEach(). Występuje między nimi tylko jedna, niewielka różnica. Metody every(), some() oraz filter() wymagają funkcji zwracających wartości typu Boolean zamiast void. function functionName(element:*, index:int, array:Array):Boolean
Przetwarzanie warunkowe za pomocą metod every(), some() oraz filter() Pierwszy zestaw metod, jaki poznasz, został zaprojektowany tak, aby umożliwić Ci sprawdzenie wszystkich wartości tablicy względem określonego przez Ciebie warunku. Metoda every() przechodzi do każdego elementu, stosując przekazaną do niej funkcję tak długo, aż jeden z elementów nie spowoduje zwrócenia przez funkcję wartości false. Metoda some() działa tak samo, ale zatrzymuje się, gdy uzyskana zostanie wartość true. Po uzyskaniu wartości „zatrzymującej” każda z tych metod zwraca ostatnią przetwarzaną wartość. Jeśli osiągnięty zostanie koniec tablicy bez wykrycia wartości zatrzymującej, zwracana jest przeciwna wartość wartości zatrzymującej — true w przypadku funkcji metody every() oraz false w przypadku metody some(). To, której metody użyjesz, zależeć będzie od tego, czy chcesz szukać wartości true, czy false. Chcesz sprawdzać, czy „każdy” (ang. every) element jest zgodny albo czy „jakiś” (ang. some) element jest zgodny? Przyjrzyjmy się teraz przykładowi. Rozpatrz następującą tablicę i funkcje filtrujące: var myArray:Array = [1,2,3,4,5]; function lessThanThree(elem:*, i:int, a:Array):Boolean { trace(i); return elem < 3; } function lessThanTen(elem:*, i:int, a:Array):Boolean { trace(i); return elem < 10; }
Rozdział 8. Używanie tablic
209
function moreThanTen(elem:*, i:int, a:Array):Boolean { trace(i); return elem > 10; }
Mamy tablicę z kilkoma liczbami całkowitymi. Każda z tych funkcji po prostu wyprowadza bieżący numer indeksu, a następnie zwraca informację — odpowiednio: czy element „jest mniejszy od trzech”, „mniejszy od dziesięciu” lub „większy od dziesięciu”. Zobaczmy, co się stanie, gdy zastosujemy te metody, a zacznijmy od every(lessThanThree): trace(myArray.every(lessThanThree)); 0 1 2 false
Sprawdzanie za pomocą every() przetwarza każdy element aż do trójki, a następnie zatrzymuje się i zwraca false, ponieważ wartość wyrażenia 3 < 3 jest fałszem. Teraz wypróbujmy metodę some(): trace(myArray.some(lessThanThree)); 0 true
Wywołanie some() nie dojdzie nawet do trójki. Zatrzymuje się przy pierwszej wartości prawdy (0 < 3). Teraz wypróbujmy przykłady, w których funkcje przechodzić będą przez każdy element: trace(myArray.every(lessThanTen)); 0 1 2 3 4 true
Każdy element listy jest mniejszy od 10, dlatego wszystkie dają wartość true. Wypróbujmy jeszcze raz metodę some(), tym razem dla moreThanTen. trace(myArray.some(moreThanTen)); 0 1 2 3 4 false
W tym przypadku żadna z wartości nie jest większa od 10 i funkcja some() wykonuje się dla wszystkich elementów, aż do końca tablicy. Te dwie funkcje są prawie identyczne i mogłyby być używane zamiennie. Sugerujemy Ci wybór tej, która wydaje się bardziej odpowiednia.
210
Część II Praca z obiektami ActionScript 3.0
Metoda filter() działa podobnie do tych dwóch metod, ale wprowadza pewną zmianę. Zamiast szukać boolowskiej wartości zatrzymującej, zawsze przetwarza całą tablicę i zwraca nową tablicę zawierającą wszystkie elementy dające wartość true: var smallNumbers:Array = myArray.filter(lessThanThree); trace(smallNumbers); // wyświetli: 1,2
Uzyskiwanie wyników za pomocą metody map() Metoda map() cechuje się trochę odmiennym podejściem od metod every(), some() oraz filter(). Mapa jest innym typem struktury danych, powszechnie stosowanym w funkcjonalnych językach programowania, i umożliwia zastosowanie funkcji dla każdego elementu tablicy. Metoda map() w ActionScript 3.0 symuluje takie działanie poprzez zastosowanie metody dla każdego elementu tablicy i zwrócenie nowej tablicy zawierającej wyniki działania funkcji każdego elementu. Poniższy kod przedstawia mapę zwracającą drugą potęgę każdego elementu. var myArray:Array = [1,2,3,4,5]; function getSquare(elem:*, i:int, a:Array):Number { if (isNaN(elem)) { return -1; } else { return elem * elem; } } var squaredArray:Array = myArray.map(getSquare); trace(myArray); // wyświetli: 1,2,3,4,5 trace(squaredArray); // wyświetli: 1,4,9,16,25
Używamy instrukcji if w połączeniu z metodą isNaN(), aby sprawdzić, czy element nie jest liczbą, ponieważ niemożliwe jest mnożenie wartości niebędących liczbami. Jeśli wartość nie jest liczbą, funkcja zwraca -1, w przeciwnym razie zwraca liczbę podniesioną do potęgi 2. Oryginalna tablica myArray pozostaje niezmieniona, a nowa tablica, squaredArray, zawiera wyniki działania funkcji getSquare() zastosowanej dla każdego elementu myArray. Może to być bardzo przydatne, gdy musisz zbierać wyniki w oparciu o duży zestaw danych.
Alternatywne typy tablic Są jeszcze inne sposoby uzyskiwania dostępu do danych tablicy niż omówione do tej pory. Niektóre z nich są prostymi technikami korzystania z tablic, inne są oddzielnymi klasami.
Praca z tablicami asocjacyjnymi Wszystkie typy dynamicznych obiektów w ActionScript, do których należy również Array, pozwalają na tworzenie „w locie” nowych zmiennych przechowywanych wewnątrz obiektu. Następnie możesz uzyskać dostęp do tych zmiennych za pomocą operatora tablicowego, przekazując jako referencję łańcuch znaków zamiast indeksu. Takie typy obiektów znane są jako tablice asocjacyjne, tablice powiązań lub tablice mieszające. Poniższy kod demonstruje tablicę asocjacyjną:
Rozdział 8. Używanie tablic
211
var lotto:Array = new Array(12, 30, 42, 6, 29, 43); lotto.description = "Losowanie lotto z tego tygodnia"; trace(lotto[3]); // wyświetli: 6 trace(lotto["description"]); // wyświetli: Losowanie lotto z tego tygodnia.
Chociaż używanie takich tablic jest możliwe i często były też one stosowane we wcześniejszych wersjach ActionScript, sugerujemy Ci użycie do takich zastosowań zamiast tablicy instancji klasy Object, słownika czy w idealnym przypadku własnej klasy. Oto kilka powodów:
Obiekty są zaprojektowane do tego typu zastosowań i oferują możliwość ich wstępnego zainicjowania.
Są mniej „kosztowne” , gdyż nie posiadają metod do manipulacji tablicami.
Wartości przechowywane według nazw mogą nie zostać uwzględnione podczas przechodzenia po nich w pętli for w tablicy.
Wartości przechowywane według nazw nie są umieszczone w jakimś konkretnym porządku, co czyni koncepcję tablicy, będącej uporządkowanym zbiorem danych, nieprawdziwą.
Takie dynamiczne przechowywanie danych nie jest poprawną praktyką obiektową i utrudnia debugowanie i sprawdzanie typów.
Używanie obiektu jako klucza wyszukiwania w słownikach ActionScript 3.0 wprowadza nowy typ tablic zwany słownikiem. Klasa Dictionary jest w rzeczywistości podklasą klasy Array. Znajdziesz ją w pakiecie flash.utils. Słownik jest prostym typem tablicy używającym do odnajdywania elementów obiektu zamiast indeksu. Umożliwia to utworzenie listy informacji, która dotyczy wielu różnych obiektów, niekoniecznie ze sobą powiązanych. W poniższym kodzie tworzymy słownik w celu przechowywania informacji o pogodzie. import flash.utils.* var var var var
notes:Dictionary = new Dictionary(); city:String = "Gliwice"; currentConditions:String = "lekkie opady"; currentTemp:Number = 26;
notes[city] = "zmierzona w parku miejskim"; notes[currentTemp] = "Celsjusza"; notes[currentConditions] = "70% prawdopodobieństwa wystąpienia opadów"; trace("Bieżąca pogoda dla", city, notes[city]); trace("Bieżąca temperatura:", currentTemp, "stopni", notes[currentTemp]); trace("Bieżące warunki:", currentConditions, "(", notes[currentConditions],")");
Wyświetlone zostaną następujące wyniki: Bieżąca pogoda dla Gliwice zmierzona w parku miejskim Bieżąca temperatura: 26 stopni Celsjusza Bieżące warunki: lekkie opady ( 70% prawdopodobieństwa wystąpienia opadów )
212
Część II Praca z obiektami ActionScript 3.0
Słowniki są szybkim i łatwym rozwiązaniem dla tego rodzaju zastosowań, jednak podobnie jak tablice asocjacyjne nie są zbyt obiektowym sposobem przechowywania informacji. Zastanów się nad użyciem statycznej metody lub właściwości dostosowanej klasy do przechowywania informacji.
Używanie tablic wielowymiarowych Nie, wielowymiarowe tablice nie są jakimś rodzajem maszyny czasowej. Wszystkie tablice, jakim do tej pory się przyglądaliśmy, są tablicami jednowymiarowymi. Oznacza to, że posiadają po jednym indeksie i reprezentują liniową listę danych — stąd jednowymiarowe. W tablicach wielowymiarowych możesz przechowywać wartości za pomocą dwóch lub więcej indeksów, co pozwala na tworzenie zbiorów danych w zbiorach danych. Z technicznego punktu widzenia wielowymiarowa tablica nie jest innym typem tablicy, ale raczej sposobem zagnieżdżania lub przechowywania tablic w sobie, co ma na celu stworzenie efektu wielowymiarowego wyszukiwania. Aby utworzyć tablicę wielowymiarową, utwórz tablicę jako jeden z elementów innej tablicy: var grid:Array = new Array(new Array(1,2), new Array(3,4)); trace(grid[0][0]); // wyświetli: 1; trace(grid[0][1]); // wyświetli: 2; trace(grid[1][0]); // wyświetli: 3; trace(grid[1][1]); // wyświetli: 4;
Powiedzmy, że chcesz przechować informacje w dwóch wymiarach — podobnie do pozycji figur na szachownicy. Szachownica jest szeroka na osiem kwadratów i wysoka na osiem kwadratów, zatem ma wymiary 8×8. Aby zapisać wszystkie wartości dla tej planszy, potrzebowałbyś ośmiu tablic o rozmiarze równym 8. Używając dwuwymiarowej tablicy, możesz zapisać te dane w postaci dwuwymiarowej siatki w jednej tablicy. Poniższy kod konfiguruje nową szachownicę poprzez utworzenie kilku tablic w jednej tablicy: const const const const const const const
k:String h:String g:String s:String w:String p:String o:String
= = = = = = =
"Król"; "Hetman"; "Goniec"; "Skoczek"; "Wieża"; "Pion"; "puste";
var chessBoard:Array = [ [w,s,g,h,k,g,s,w], [p,p,p,p,p,p,p,p], [o,o,o,o,o,o,o,o], [o,o,o,o,o,o,o,o], [o,o,o,o,o,o,o,o], [o,o,o,o,o,o,o,o], [p,p,p,p,p,p,p,p], [w,s,g,h,k,g,s,w] ]; trace("Figura na pozycji (0,2) trace("Figura na pozycji (7,4) trace("Figura na pozycji (4,3) trace("Figura na pozycji (7,7)
:", :", :", :",
chessBoard[0][2]); chessBoard[7][4]); chessBoard[4][3]); chessBoard[7][7]);
Rozdział 8. Używanie tablic
213
Kod ten wyświetli: Figura Figura Figura Figura
na na na na
pozycji pozycji pozycji pozycji
(0,2) (7,4) (4,3) (7,7)
: : : :
Goniec Król puste Wieża
W przykładzie tym utworzyliśmy siatkę danych o wymiarach 8×8, reprezentującą każde położenie na szachownicy. Za pomocą stałych reprezentujących poszczególne figury oraz skróconej składni literału tablicowego dodaliśmy do szachownicy wszystkie figury. W celu odszukiwania wartości traktujemy współrzędne x i y jako indeksy. Gdy mówimy chess board[7][4], w rzeczywistości mamy na myśli: pobierz wartość z siódmego indeksu tablicy chessboard, która w tym przypadku jest inną tablicą. Następnie wybieramy czwarty indeks zwracanej tablicy. Podczas przyglądania się numerom dwóch indeksów można stwierdzić, że skrócona składnia wygląda na wygodną w stosowaniu. Możesz używać takich tablic lub tworzyć bardziej złożone trzy-, cztero- lub nawet siedemnastowymiarowe tablice, gdy tylko będziesz miał do czynienia ze złożonym zbiorem danych geometrycznych lub z danymi odwołującymi się do siebie wzajemnie.
Podsumowanie
Tablice są uporządkowanymi listami danych, wykorzystującymi numery indeksów do odnajdywania wartości zwanych elementami.
Możesz tworzyć nowe tablice poprzez użycie konstruktora Array lub literału tablicowego [].
Twórz łańcuchy znaków z wartości tablicowych za pomocą metod toString() oraz join().
Uzyskuj dostęp do wartości wewnątrz tablicy poprzez użycie operatora tablicowego ([]).
Wyszukiwanie wartości w tablicy z wykorzystaniem metody indexOf() jest bardzo proste.
Przeprowadzaj operacje na każdym elemencie tablicy za pomocą metod forEach(), map(), filter(), every() oraz some().
Wstawiaj wartości do tablicy lub usuwaj z niej mniejsze fragmenty za pomocą metod splice() i slice().
214
Część II Praca z obiektami ActionScript 3.0
Rozdział 9.
Używanie obiektów W tym rozdziale:
Używanie obiektów i słowników do manipulowania tablicami asocjacyjnymi
Używanie klasy Object jako klasy głównej
Stosowanie obiektów do różnych zadań
Wszystkie klasy rozszerzają klasę Object — jest ona główną klasą w hierarchii klas w ActionScript. Instancje klasy Object mogą być naprawdę przydatne jako struktura danych. W rozdziale 8. pokazano, jak tablice mogą być wykorzystywane do przechowywania danych asocjacyjnych. W tym rozdziale zobaczysz, jak instancje klasy Object oraz inne klasy mogą zostać wykorzystane do przechowywania i pozyskiwania takich informacji. Poznasz też inne zastosowania klasy Object, w których okaże się bardzo przydatna.
Praca z obiektami Klasa Object jest podstawą wszystkich hierarchii typów wszystkich klas. Innymi słowy, każdy obiekt w ActionScript 3.0 dziedziczy z klasy Object. Sama klasa Object nie robi zbyt wiele i tak właściwie byłaby najprostszym elementem, jaki mógłbyś utworzyć. Posiada jednak jedną interesującą właściwość — klasa Object jest jedną z nielicznych klas dynamicznych.
Klasy dynamiczne Klasy dynamiczne można rozszerzać w trakcie wykonywania o nowe właściwości i metody. Oznacza to, że dla danych i operacji definiujących obiekt możliwa jest zmiany ich nazw, nadpisanie ich lub rozbudowanie w czasie wykonywania programu. Gdyby Forrest Gump był programistą, prawdopodobnie powiedziałby Ci, że obiekty dynamiczne są jak pudełko czekoladek: nigdy nie wiesz, na co trafisz. Czyni to programowanie z wykorzystaniem klas dynamicznych przeważnie kiepskim wyborem. Jeśli programowałbyś zestaw obiadowy z wykorzystaniem dynamicznych klas, jakiś niekontrolowany przez Ciebie kod mógłby nadpisać Twoją właściwość saltShaker tak, aby zawierała paprykę habanero, co z pewnością zrujnowałoby nawet idealnie przygotowany posiłek. W tak chaotycznym świecie nie
216
Część II Praca z obiektami ActionScript 3.0
da się żyć, dlatego każda klasa napisana przez nas w tej książce jest zapieczętowana lub zamknięta na modyfikacje w czasie wykonywania programu. Bez tej gwarancji nie jesteśmy w stanie zapewnić zgodności z regułami programowania obiektowego. Klasy są domyślnie zapieczętowane, jednak możesz utworzyć klasę dynamiczną (niezapieczętowaną), używając słowa kluczowego dynamic przed definicją klasy: public dynamic class UnsealedClass
Połączenie takiej nieograniczonej rozszerzalności z dziedzicznie pustą klasą Object dostarcza doskonałą metodę przechowywania. Oto dlaczego wspominamy o klasie Object jako o typie danych. Potrzebujemy, aby jej podklasy były w stanie robić prawie wszystko, gdyż sama klasa Object nie robi prawie nic. Możesz myśleć o klasie Object jak o glinie, z której w świecie ActionScript formowana jest cała reszta obiektów. Poprzez tworzenie klas rzeźbisz z tej gliny formę i wypalasz ją, aby wszystkie obiekty miały ten sam kształt. W tym rozdziale zobaczysz też, że możesz postanowić użyć samej gliny w jej surowej formie i utworzyć to, co jest Ci potrzebne do jednorazowego użycia. Jeśli chcesz używać obiektów typu Object, nie musisz przecież wiedzieć, jak powstały. Instancja klasy Object może być na tyle prosta, że służyć będzie jedynie jako przechowalnia właściwości.
Tworzenie obiektów Nowy obiekt możesz utworzyć za pomocą jego konstruktora. Konstruktor klasy Object nie pobiera żadnych parametrów i tworzy pustą instancję klasy Object: var o:Object = new Object();
Typ Object, podobnie jak Number czy String, również posiada formę literału. Możesz utworzyć instancję Object od podstaw z predefiniowanymi wartościami poprzez zwykłe napisanie jej w postaci literału. Literał dla typu Object jest rozdzielaną przecinkami listą par nazwa i wartość, zawartą w klamrowych nawiasach: var o:Object = {name: "Roman", hair: 0xff0000, hobbies: myHobbies};
Nazwy właściwości nie muszą być brane w cudzysłowy, a ich wartości mogą być dowolnego typu. Wartości mogą być również referencjami do innych obiektów. W tym przykładzie przypisujemy właściwości hobbies obiektu wartość zmiennej myHobbies. Nie powoduje to skopiowania wartości zmiennej do obiektu, ale przechowywanie referencji. Jeśli zainteresowania przechowywane w tej zmiennej ulegną zmianie, to podczas pobierania zainteresowań obiekt odwoła się do wartości zmiennej i będzie posiadać najnowsze informacje. Właściwości obiektu mogą być dowolnym łańcuchem znaków, w tym łańcuchami znaków niebędącymi poprawnymi identyfikatorami. Oznacza to, że możliwe jest przechowanie w obiekcie takiej właściwości jak 99butelek, ale utworzenie zmiennej o takiej nazwie już nie jest możliwe, ponieważ identyfikatory nie mogą zaczynać się od liczby.
Rozdział 9. Używanie obiektów
217
Dostęp do właściwości obiektu Dostęp do właściwości obiektu, podobnie jak do właściwości dowolnej instancji klasy, możesz uzyskać za pomocą notacji kropkowej lub notacji tablicowej. Różnica między nimi (poza składnią) polega na tym, że notacji kropkowej możesz używać tylko dla nazw właściwości będących również poprawnymi identyfikatorami. Możesz używać tych sposobów do odczytu i zapisu w obiektach. var shape:Object = new Object(); shape.name = "Sześciokąt"; shape.sides = 6; shape["color"] = 0x00ff00; shape["stroke weight"] = 2; shape["describe"] = function():String {return this.name;}; trace(shape.describe() + " ma " + shape.sides + " boków."); // wyświetli: Sześciokąt ma 6 boków.
Ten przykład pokazuje, że obiekty funkcji tak jak i inne typy danych mogą być przechowywane w instancji obiektu. Nie jest to jakiś specjalny przypadek, ale po prostu kolejny zapisany obiekt. Zapis shape.describe lub shape["describe"] odwołuje się do obiektu funkcji przechowywanego jako describe, a wstawienie nawiasów spowoduje wywołanie tej funkcji. Pewnie zauważyłeś, że notacja wykorzystująca nawiasy kwadratowe wygląda tak samo jak notacja tablicowa i możemy używać tego „cukru syntaktycznego” do wykorzystywania obiektów jako tablic asocjacyjnych.
toString() Jedna z metod zdefiniowanych dla klasy Object okaże się przydatna nie tylko dla instancji klasy Object, ale również dla wszystkich jej podklas. Metoda toString() zdefiniowana jest w klasie Object, dlatego dostępna jest dla wszystkich klas. Używa się jej do przedstawiania instancji w postaci łańcucha znaków. Możesz przesłonić tę metodę w swoich klasach w celu stworzenia dobrej reprezentacji tekstowej dla powiadomień i logów. Problem związany ze stosowaniem metody toString() polega na tym, że przechowywanie wartości w instancji klasy Object o nazwie „toString” przesłania tę funkcję. Jeśli taka sytuacja jest prawdopodobna, unikaj wywoływania toString() dla obiektów, które mogą przesłaniać tę właściwość.
Używanie instancji klasy Object jako tablicy asocjacyjnej Tablica asocjacyjna jest strukturą danych przechowującą elementy według indeksów. Działa podobnie do szafki kartotekowej, ponieważ znając nazwę szukanego elementu, możesz z łatwością go odnaleźć. Nazwa lub symbol na brzegu teczki w Twojej szafce nazywane są kluczem, a sam element lub odnaleziona przez Ciebie teczka nazywana jest wartością. Tablice asocjacyjne podobnie jak szafka kartotekowa pozwalają na dodawanie nowych
218
Część II Praca z obiektami ActionScript 3.0
teczek, odnajdywanie teczek po nazwie, usunięcie teczki z szafki, sprawdzenie, czy teczka o podanej nazwie istnieje i przewertowanie wszystkich teczek w szafce. Jednak podstawową operacją jest wyszukiwanie, a przechowywanie wartości według nazw czyni tablicę asocjacyjną. Tablice asocjacyjne zachowują relację typu wiele do jednego pomiędzy kluczami a wartościami. Istotne jest to, że każdy klucz zawsze odwołuje się do pojedynczej wartości. Jednak możliwe jest przechowywanie pewnej wartości pod wieloma kluczami. Np. możesz chcieć utworzyć długi, cienki obiekt Noodle i zapisać go pod nazwą „Angel Hair”, ale również pod „Cappellini”. Obie nazwy odwołują się do tego samego typu makaronu i oba klucze mogą odwoływać się do tej samej wartości, instancji Noodle. Być może spotkałeś się już z tym, że tablice asocjacyjne nazywa się tablicami haszującymi, mapami czy słownikami. Są to nazwy tego samego typu danych. Pojęcia „tablica haszująca”, „hasz” oraz „mapa” odwołują się do konkretnego sposobu implementacji tej struktury danych; stosuje się w nim funkcję haszującą dla klucza w celu równomiernego rozmieszczenia wartości w pamięci i późniejszego ich odnajdywania. Odbywa się to wewnątrz maszyny wirtualnej ActionScript, dlatego dla naszych potrzeb musimy jedynie wiedzieć, że instancja klasy Object może zostać wykorzystana w roli tablicy asocjacyjnej. Taki rodzaj danych asocjacyjnych jest bardzo przydatny i znaleźć go można wokół nas — w powszechnych problemach programistycznych, udostępniony do prostego przeglądania w internecie oraz w metaforach prawdziwego świata, które możemy stosować do tworzenia struktury kodu. Np. większość wyszukiwarek internetowych pokazuje Ci dane asocjacyjne już w samym adresie strony. Rozpatrz URL www.google.com/search?q=hashtable&safe= active. Po znaku zapytania (?) występuje asocjacyjna tablica kluczy i wartości. Zapytanie zostanie zapisane jako klucz q oraz wartość hashtable, ponieważ szukamy informacji o tablicach haszujących. Parametr związany z funkcją filtrującą zawartość w wyszukiwarce nazywa się safe, a jego wartość to active. Stosujemy go, ponieważ nie chcemy uzyskiwać nieodpowiednich wyników. Kto wie, jakiego rodzaju lubieżne przykłady mogą zamieszczać uczniowie na zajęciach z informatyki? Ten przykład używa par klucz i wartość do reprezentowania parametrów, jak każdy inny formularz w internecie wykorzystujący metodę HTTP GET. Jest to jedna z kluczowych technik organizacyjnych, służących do odwoływania się do złożonych danych poprzez referencję, czym właśnie zajmują się tablice asocjacyjne. W przykładach z ostatniego podrozdziału już zademonstrowaliśmy większość z ważniejszych właściwości tablicy asocjacyjnej opartej na klasie Object. Zapisywaliśmy i pobieraliśmy właściwości lub wartości za pomocą notacji kropkowej lub tablicowej. W tym podrozdziale dowiesz się, jak sprawdzać, czy dane istnieją, jak iterować po kolejnych wartościach oraz jak usuwać wartości.
Porównywanie tablic, obiektów i słowników W rozdziale 8. wyjaśniono, jak używać tablicy do przechowywania tablicy asocjacyjnej. Zalecamy Ci jednak wykorzystywanie tablic jedynie dla danych indeksowanych numerycznie. Używanie obiektów klasy Array daje wrażenie, że do danych można uzyskiwać dostęp za pomocą numerycznych indeksów oraz że możliwe jest wykorzystanie pożytecznych metod tablicowych, jak chociażby indexOf() oraz splice(). Prawda jest jednak taka, że żadna z tych metod nie działa dla właściwości przechowywanych według kluczy zamiast
Rozdział 9. Używanie obiektów
219
indeksów. Mógłbyś wywołać jeszcze większe zamieszanie poprzez zachowanie obu wartości w instancji tablicy po indeksie oraz kluczu asocjacyjnym. Rozpatrz możliwość przechowywania wartości pod kluczami takimi jak 12. Wykorzystanie tablicy do uzyskania dostępu do takiej właściwości (nie wspominając nawet o jej użyciu) mogłoby wprowadzić zamieszanie w Twoim kodzie. Używaj instancji klasy Object do przechowywania wartości według kluczy, a instancji klasy Array do przechowywania wartości według numerycznych indeksów. Takie obiekty idealnie nadają się do przechowywania par klucz i wartość. Wykorzystanie notacji tablicowej z instancją klasy Object może wyglądać tak, jak odwoływanie się do indeksu tablicy poprzez liczbę, przez co możemy traktować ją jak instancję klasy Array, ale bez niepotrzebnego zamieszania związanego z obsługiwaniem przez nią również numerycznych indeksów. ActionScript 3.0 wprowadza nowy typ tablicy asocjacyjnej o nazwie Dictionary (słownik). Odnajdziesz go w pakiecie flash.utils.
Słowniki mogą być również używane do przechowywania par klucz i wartość. Zostały jednak znacznie ulepszone. Instancje klasy Object używają jako kluczy łańcuchów znaków. Dla większości zastosowań okazuje się to wystarczająco dobrym rozwiązaniem, ale powiedzmy, że potrzebujesz szybkiego i bezpardonowego sposobu zapamiętywania w klasach jakichś dodatkowych informacji. Łańcuchy znaków mogą czasem być odpowiednim typem do tego celu. Możesz np. spotkać grupę nowych ludzi na imprezie. var guy1:Person = new Person("Robert"); var guy2:Person = new Person("Maciek"); var girl1:Persion = new Person("Joanna");
Próbujesz zapamiętać jakieś rzeczy na temat każdej osoby, zatem używasz instancji klasy Object do przechowywania informacji obok jej imienia. var notes:Object = new Object(); notes["Robert"] = "Lubi gry"; notes["Maciek"] = "Bardzo zorganizowany"; notes["Joanna"] = "Lubi rysować";
Działa to całkiem dobrze, dopóki nie pojawi się inna osoba o imieniu Joanna. Nagle kończy się możliwość niezależnego śledzenia obu tych osób. Albo, nie daj Boże, zapomnisz któregoś z imion, a bez imienia nie jesteś w stanie odnaleźć niczego. var girl2:Person = new Person("Joanna");
Byłaby to idealna okazja do użycia obiektu Dictionary. Obiekty klasy Dictionary mogą stosować obiekty jako klucze. Zamiast imienia wyszukałbyś „całą” osobę, aby uzyskać wartość powiązaną z tą osobą. import flash.utils.Dictionary; var notes:Dictionary = new Dictionary(); notes[guy1] = "Lubi gry"; notes[guy2] = "Bardzo zorganizowany"; notes[girl1] = "Lubi rysować"; notes[girl2] = "Śpiewaczka";
220
Część II Praca z obiektami ActionScript 3.0
Używając obiektów jako kluczy, możesz w bardzo prosty sposób powiązać je z innymi danymi. Aby uniknąć użycia obiektów, możemy użyć unikalnego ID jako klucza tekstowego, ale nie zawsze jest to możliwe i łatwiejsze może być po prostu zapisanie obiektu. Jeśli chcesz zapewnić, aby obiekty używane przez Ciebie jako klucze nie były niepotrzebnie przechowywane przez obiekt Dictionary, możesz przekazać true jako parametr weakKeys do konstruktora Dictionary: new Dictionary(true);
Wartość domyślna tego parametru wynosi false. Jeśli chcesz uzyskać więcej informacji na ten temat, zajrzyj do omówienia słabych odwołań w odniesieniu do obsługi zdarzeń w rozdziale 16. Słowniki posiadają tę samą składnię dla wszystkich operacji (wstawiania, usuwania, ustawiania oraz pobierania), co obiekty klasy Object, zatem techniki, jakie poznasz w tym rozdziale dla klasy Object, będą również działać dla instancji klasy Dictionary.
Testowanie istnienia Możesz sprawdzać istnienie klucza w tablicy asocjacyjnej przez przeprowadzenie wyszukiwania lub za pomocą operatora in. Użyj operatora in w celu sprawdzenia, czy właściwość istnieje.
Operator in umożliwia sprawdzenie istnienia właściwości, metod czy wartości instancji, o ile można do nich uzyskać dostęp. Operator ten zwraca wartość typu Boolean. Możesz użyć operatora in do sprawdzenia, czy klucz jest powiązany z jakąś wartością z tablicy asocjacyjnej: var notes:Object = {Robert: "jestemtutaj"}; "Robert" in notes; // zwraca true "Janusz" in notes; // zwraca false
Możesz też użyć tego operatora do sprawdzania instancji klas na ewentualność obsługi przez nie metod i właściwości: class Person { public var name:String; private var SSN:Number; public function Person(name:String) { this.name = name; } } var sprite:Sprite = new Sprite(); trace("alpha" in sprite); // wyświetli true trace("getChildAt" in sprite); // wyświetli true var person:Person = new Person("Bartek"); trace("name" in person); // wyświetli true trace("SSN" in person); // wyświetli false
Rozdział 9. Używanie obiektów
221
Ten przykład ilustruje odnajdywanie metod oraz właściwości zarówno klas wbudowanych, jak i własnych. Pokazuje też, że in respektuje widoczność właściwości. W dalszym ciągu działa również stara metoda testowania istnienia klucza w tablicy asocjacyjnej, polegająca na prostym sprawdzeniu, czy dany klucz występuje: trace(notes["Janusz"]); // undefined if (notes["Janusz"]) { trace(notes["Janusz"]); } else { trace("Jeszcze nie spotkałem Janusza."); } // wyświetli: Jeszcze nie spotkałem Janusza.
Metoda ta może być trochę niebezpieczna, ponieważ wiele wartości może wymuszać wartość false. Gdy powiesz np., że Janusz jest zerem (notes["Janusz"] = 0), wykonana zostanie gałąź else. Jeśli chcesz zachować bezpieczeństwo, porównuj efekty swojego wyszukiwania bezpośrednio z wartością undefined lub używaj operatora in.
Usuwanie właściwości Operator delete może zostać użyty do usuwania par klucz i wartość z tablicy asocjacyjnej. Zastosuj operator delete do wartości, tak jakbyś chciał ją pobrać, a zarówno klucz, jak i wartość zostaną usunięte z tablicy asocjacyjnej: var pastas:Object = {tortellini: 2, gemelli: 14, spaghetti: 9}; trace(pastas["spaghetti"]); // wyświetli 9 delete pastas["spaghetti"]; trace(pastas["spaghetti"]); // wyświetli undefined
Operator delete działa również dla dynamicznie dodanych właściwości i metod instancji klas dynamicznych. Operator delete zwraca true, jeśli usuwanie zakończy się powodzeniem. Po użyciu delete dla elementu tablicy właściwość length nie ulega zmianie. Nie możesz usuwać właściwości instancji, które nie są dynamiczne, czy zmiennych lokalnych. Aby zwolnić obiekty, możesz usunąć wszystkie referencje do nich, np. poprzez przypisanie zmiennym wartości null.
Iterowanie Możesz iterować po parach klucz i wartość w obiekcie za pomocą pętli for..in oraz for each..in, opisanych w rozdziale 2. Użyj pętli for..in, gdy potrzebujesz dostępu do kluczy, a for each..in, gdy interesują Cię tylko wartości: for (var name:String in notes) { trace("Uwagi na temat " + name + ": " + notes[name]); } // wyświetli: Uwagi na temat Robert: lubi gry... itd.
Tutaj kluczem jest imię, a uwagi, jakie zebraliśmy na temat osoby, są wartością.
222
Część II Praca z obiektami ActionScript 3.0
Użycie obiektów dla nazwanych argumentów ActionScript 3.0 oferuj sporą elastyczność podczas przekazywania parametrów do funkcji. Możesz opuszczać argumenty z domyślnymi wartościami i akceptować listy argumentów o zmiennym rozmiarze za pomocą argumentu reszty (...). Od czasu do czasu może okazać się konieczne akceptowanie przez funkcję więcej niż kilku możliwych argumentów, w przypadku czego rozpoznanie przeznaczenia argumentu wyłącznie na podstawie jego pozycji na liście argumentów może być zadaniem pamięciowym. W takich przypadkach, gdy liczba parametrów dla funkcji nie wynika z wady projektu, możesz chcieć pracować z nazwanymi argumentami. ActionScript 3.0 nie używa nazwanych argumentów, ale instancja klasy Object może zostać użyta jako zmienna lista argumentów, używająca kluczy jako nazw argumentów. Ponieważ nie możesz sprawdzić poprawności przekazywanego obiektu podczas kompilacji, najlepiej używać instancji klasy Object jedynie dla nazwanych argumentów posiadających domyślne wartości. Poniższy kod ustawia konkretny styl linii podczas przygotowań do rysowania kresek na obiekcie typu Sprite: var sprite:Sprite = new Sprite(); var g:Graphics = sprite.graphics; g.lineStyle(1, 0xff0000, 1, true, "none", "round", "round", 20); g.lineStyle(1, 0xff0000, 1, true, LineScaleMode.NONE, CapsStyle.ROUND, JointStyle.ROUND, 20);
Dwa ostatnie wiersze są identyczne. Wywołanie funkcji jest prawie że niezrozumiałe, gdy dla trzech parametrów znajdujących się pod koniec wywołania używamy łańcuchów znaków. Gdy stosuje się dla tych zmiennych stałe statyczne, czytelność zapisu znacznie wzrasta. Powiedzmy, że ponownie napisaliśmy funkcję lineStyle(), aby pobierała parametr typu Object. Wywołanie mogłoby wyglądać zupełnie inaczej: g.lineStyle({ thickness: 1, color: 0xff0000, alpha: 1, pixelHinting: true, scaleMode: LineScaleMode.NONE, caps: CapsStyle.ROUND, joints: JointStyle.ROUND, miterLimit: 20 });
To użycie jednorazowego lub anonimowego obiektu w celu nazwania parametrów może poprawić czytelność kodu, ale kosztem sprawdzania argumentów podczas kompilacji. Zalecamy ostrożność podczas stosowania tego rozwiązania.
Rozdział 9. Używanie obiektów
223
Używanie obiektów jako zagnieżdżonych danych Poprzez wstawianie obiektów do obiektów możesz utworzyć zagnieżdżone struktury przypominające drzewa. Stosując notację kropkową, z łatwością można się przedostać w głąb zagnieżdżonych drzew instancji klasy Object. plants.veggies.underground.carrot;
XML jako obiekty W ActionScript 1.0 oraz w ActionScript 2.0 nie było niczym nadzwyczajnym konwertowanie struktury XML na drzewa obiektów w celu zapewnienia sobie większej wygody, ponieważ poruszanie się po XML było trochę niewygodne. Po pojawieniu się E4X przemieszczanie po XML stało się bardzo proste. E4X pozwala również obsługiwać dane strukturalne z o wiele większą elegancją i wyszukaniem niż obiekty zagnieżdżone. Zalecamy zawsze preferowanie XML dla danych o strukturze drzewiastej. Rozdział 10. zawiera dokładniejszy opis pracy z XML.
JSON Składnia wykorzystana do deklarowania literałów instancji klasy Object jest wystarczająco prosta i wydajna dla większości zastosowań realizowanych przez programistów sieciowych jako lżejsza alternatywa dla XML. Interpretery JavaScript po prostu muszą być w stanie za pomocą funkcji eval() przekształcić łańcuch reprezentujący strukturę obiektu w ten obiekt. Niestety, ta sztuczka nie działa w ActionScript 3.0, ponieważ Flash Player nie kompiluje ani nie uruchamia kodu ActionScript w czasie wykonywania programu. Dlatego E4X oraz brak interpretacji eval() są dwoma wystarczającymi powodami do preferowania XML dla danych zagnieżdżonych. Jeśli musisz używać JSON, Adobe oferuje parser JSON w bibliotece corelib, który dostępny jest pod adresem http://labs.adobe.com/wiki/index.php/ActionScript_3:resources:apis: libraries#corelib.
Podsumowanie
Wszystkie klasy rozszerzają klasę Object.
Object
to pusta, dynamiczna klasa.
Możesz tworzyć i modyfikować właściwości oraz metody dynamicznych klas w czasie wykonywania programu.
Zarówno instancje klasy Object, jak i Dictionary mogą działać jak tablice asocjacyjne.
224
Część II Praca z obiektami ActionScript 3.0
Tablice asocjacyjne przechowują pary klucz i wartość. W instancjach klasy Object klucze muszą być łańcuchami znaków. W instancjach klasy Dictionary mogą być obiektami.
Anonimowe obiekty mogą być używane do przekazywania nazwanych argumentów.
JSON przechowuje zagnieżdżone dane w składni literału obiektowego, ale w E4X bardziej przydatny jest XML.
Rozdział 10.
Praca z XML W tym rozdziale:
Podstawy XML oraz nowego systemu E4X
Tworzenie obiektów XML za pomocą ActionScript
Uzyskiwanie dostępu do danych XML za pomocą składni kropkowej i operatora @
Filtrowanie potrzebnych informacji z drzewa XML
Manipulowanie XML za pomocą wbudowanych metod
Praca z przestrzeniami nazw, komentarzami i instrukcjami przetwarzania
Jedną z być może najbardziej znaczących zmian w trzeciej wersji ActionScript jest wprowadzenie E4X, kompletnie zmienionego systemu obsługi danych XML, opartego na standardzie ECMAScript. Umożliwia on programistom bezproblemową pracę z XML w ich programach. W ActionScript 3.0 dane mogą być zapisywane bezpośrednio w kodzie za pomocą literałów XML, które są automatycznie parsowane.
Rozpoczęcie pracy z XML w ActionScript XML jest typem znacznikowym służącym do tworzenia hierarchicznych struktur danych, wykorzystywanym powszechnie do niezliczonych zastosowań. Ta książka zakłada, że posiadasz podstawową wiedzę na temat struktury i składni XML. Jeśli nie znasz XML, zapoznaj się z tutorialem udostępnianym przez organizację W3C Schools (www.w3schools. com/xml/) lub ze stroną Wikipedii na temat XML (http://pl.wikipedia.org/wiki/XML). Chociaż XML rozpowszechniany jest w różnych odmianach, każdy kod XML posiada taką samą podstawową strukturę:
Zawartość węzła
226
Część II Praca z obiektami ActionScript 3.0
Dane zawarte są w węzłach, inaczej zwanych elementami, i zamknięte pomiędzy znacznikami rozpoczynającym i kończącym. Niektóre elementy posiadają atrybuty dostarczane w postaci par nazwa i wartość wewnątrz ich znaczników rozpoczynających, zawierające dodatkowe informacje na temat tych elementów. Niektóre elementy mogą zawierać węzły tekstowe, które zawierają dane przechowywane wewnątrz elementu. XML może zawierać również węzły z deklaracją przestrzeni nazw, komentarzami lub instrukcjami przetwarzania.
Początki E4X Jeśli pracowałeś z Flashem przez ostatnie dziesięć lat, pewnie nie uznajesz platformy Flash za trzymającą się otwartych standardów. Jednak ActionScript 3.0 jest jedną z najdokładniejszych implementacji standardu ECMAScript. Nazwa E4X jest skrótem dla ECMAScript for XML, implementacji standardu zdefiniowanego w ECMA-357. Jest to ten sam standard dostępowy, którego używa JavaScript 2.0. Składnia nowej klasy XML dla programistów ActionScript jest prosta i intuicyjna. Może wydać Ci się podobna do standardu XPath lub narzędzi XML dostarczanych przez firmy trzecie dla AS2. Oferuje uproszczoną notację skrótową, która umożliwia uzyskiwanie dostępu do konkretnych węzłów lub zestawów węzłów poprzez nazwę lub indeks, bez potrzeby tworzenia własnych, skomplikowanych, rekurencyjnych funkcji. Definiuje również metody i właściwości służące do uzyskiwania dostępu do wszystkich części obiektu XML, w tym komentarzy, przestrzeni nazw i instrukcji przetwarzania.
Praca z literałami XML Oprócz nowych klas XML, AS3 obsługuje również literały XML lub wartości stałych, które mogą być dodawane do kodu i interpretowane w ten sam sposób, co łańcuchy znaków i liczby. Dzięki temu możesz dodawać dane XML bezpośrednio w swoim kodzie bez konieczności parsowania łańcuchów znaków lub wczytywania danych ze źródeł zewnętrznych. Poniżej znajdziesz przykład literału XML przypisywanego do obiektu XML. Zwróć uwagę na to, że wokół danych nie ma żadnego konstruktora czy cudzysłowów — możesz pisać XML bezpośrednio w swoim kodzie. var employeeList:XML =
Al Bundy
Peggy Bundy
Reżyser
;
Przykład ten przedstawia dane XML dodawane bezpośrednio do zmiennej o nazwie employeeList.
Rozdział 10. Praca z XML
227
AS3 oferuje możliwość wczytywania XML ze źródeł zewnętrznych. Jednak ze względu na czytelność i prostotę kodu używamy w tym rozdziale literałów. Więcej na temat wczytywania danych z zewnętrznych źródeł dowiesz się z rozdziału 22.
Używanie wyrażeń wewnątrz literałów Możesz też osadzać kod ActionScript bezpośrednio w danych XML poprzez zamykanie tego kodu w nawiasach klamrowych. Pozwoli Ci to na wypełnienie zestawu danych dynamicznie wygenerowanymi wartościami: var squareLength:Number = 10; var circleRadius:Number = 5; var shapeData:XML =
; trace(shapeData.toXMLString());
Ostatnia metoda, toXMLString(), konwertuje dane XML zapisane w zmiennej shapeData na ich odpowiednik tekstowy, zachowując strukturę XML. Daje to w rezultacie następujące dane XML:
Krótkie wprowadzenie do operatorów i składni E4X E4X definiuje kilka różnych sposobów uzyskiwania dostępu i edytowania danych wewnątrz obiektów XML: poprzez użycie metod klas XML oraz XMLList, a także przez użycie specjalnego zestawu operatorów do pracy z XML. Technicznie rzecz biorąc, nawiasy wokół kodu XML są operatorami. Oficjalna nazwa ostrych nawiasów to literal tag delimiter. Występuje jeszcze wiele innych operatorów, ale większość z nich ma pomagać Ci w tworzeniu struktur XML wewnątrz kodu. Przyjrzymy się teraz niektórym z bardziej powszechnie używanych operatorów dostępu. Należą do nich kropka (.), identyfikator atrybutu (@) oraz nawiasy kwadratowe ([]). Kropka umożliwia dostęp do elementów Twojego kodu XML, identyfikator atrybutu pozwala Ci na dostęp do atrybutów elementu, a nawiasy kwadratowe dają dostęp do właściwości poprzez jej nazwę lub indeks. W praktyce można używać ich w sposób bardzo podobny do użycia kropki dla właściwości i metod obiektu oraz nawiasów kwadratowych dla tablic. W poniższym przykładzie można z łatwością odnajdywać dane głęboko zagnieżdżone w drzewie XML. var myXML:XML =
foxtrott
228
Część II Praca z obiektami ActionScript 3.0
juliett
; trace(myXML.bravo[1].golf); // wyświetli: juliett trace(myXML.bravo[0].charlie.@delta); // wyświetli: echo
Wszystkie te operatory (oraz odpowiadające im metody) omówimy wkrótce dokładniej.
Klasy XML Nowa implementacja XML używa kilku klas będących nowością w ActionScript 3.0. Każda klasa reprezentuje inny typ danych używany podczas pracy z danymi XML. Wszystkie znajdują się na najwyższym poziomie API ActionScript, dzięki czemu nie będziesz musiał ich importować przed użyciem. Przyjrzymy się krótko zastosowaniu każdej z tych klas.
XML Obiekt XML reprezentuje kawałek prostych danych XML, jak element, węzeł tekstowy, komentarz czy instrukcja przetwarzania. Definiowanie tych pozornie różnych węzłów w postaci obiektów XML umożliwia używanie metod do manipulowania danymi XML dla wszystkich z nich, niezależnie od ich typu. W większości przypadków zobaczysz te wartości zagnieżdżone wewnątrz elementów w celu tworzenia złożonych wartości przechowywanych w jednej zmiennej. Przykładem złożonego obiektu XML zawierającego jeden element oraz węzeł tekstowy może być: var myXML:XML = Witaj świecie!;
XMLList Obiekt XMLList jest numerowanym zestawem, bardzo podobnym do tablicy zawierającej obiekty XML. Dane XML w nim zawarte mogą być jednym lub wieloma obiektami XML, kawałkami obiektów XML lub pojedynczymi węzłami. Kilka z metod klasy XML, jak np. children(), zwraca wyniki w postaci obiektów XMLList. Obiekt taki posiada wiele wspólnych metod z klasą XML, co umożliwia przeprowadzanie większości operacji zamiennie. Podobnie jak tablice, tak i obiekty XMLList posiadają w sobie indeksy dla każdego obiektu XML oraz metodę length() pokazującą łączną liczbę elementów. Poniżej znajduje się przykład obiektu XMLList zawierającego wiele węzłów . Zauważ, że elementy w obiekcie XMLList istnieją obok siebie bez posiadania węzła rodzica (), zatem XMLList jest płaskim zestawem węzłów, a nie hierarchią. var list:XML =
; var items:XMLList = list.item; trace(items); // wyświetli: //
//
trace(items.length()); // wyświetli: 3 trace(items[1].toXMLString()); // wyświetli:
Rozdział 10. Praca z XML
229
Namespace Obiekt Namespace definiuje przestrzeń nazw XML dla obiektu XML. W XML przestrzeni nazw używa się do definiowania pewnych znaczników będących częścią jakiejś grupy lub języka. Np. wszystkie komponenty frameworku Flex 3 są zdefiniowane w przestrzeni nazw mx, a wywołania SOAP używają przestrzeni nazw soap. Przestrzenie nazw omówimy dokładniej w dalszej części tego rozdziału.
QName Obiekt QName reprezentuje relację między kwalifikowaną przestrzenią nazw URI oraz identyfikatorem lokalnej przestrzeni nazw. W przypadku frameworku Flex używa się go do łączenia URI www.adobe.com/2006/xml z lokalnym identyfikatorem mx. Jeśli nie widzisz w tym żadnego sensu, nie przejmuj się. Obiekty QName omówimy przy okazji omawiania przestrzeni nazw w dalszej części tego rozdziału. W AS3 klasy XML najwyższego poziomu obsługują dane XML w całkowicie nowy sposób. Starsze klasy XML z wcześniejszych wersji ActionScript zostały jednak dołączone, głównie ze względu na zapewnienie obsługi zgodności wstecz. Stara klasa XML nazywa się teraz XMLDocument i znajduje się w pakiecie flash.xml, co pozwala uniknąć zamieszania. Nie będziemy opisywać tych klas XML w tym rozdziale. Więcej informacji na temat pakietu flash.xml znajdziesz w opisie języka ActionScript 3.0.
Dostęp do wartości za pomocą E4X E4X udostępnia sposoby używania danych w plikach XML będące znacznym ulepszeniem w stosunku do metod firstChild() oraz nextSibling() używanych w ActionScript 2.0.
Używanie operatora kropki do uzyskiwania dostępu do elementów Operator kropki jest najprostszym narzędziem w arsenale E4X i najlepiej poznać go na przykładzie. Ten kod definiuje XML dla listy filmów. Wykorzystamy go w kilku kolejnych przykładach: var movieList:XML = Moje ulubione filmy
Tytus 1999 Julie Taymor
Rushmore 1998 Wes Anderson
230
Część II Praca z obiektami ActionScript 3.0
Annie Hall 1977 Woody Allen
;
Używając operatora kropki, możesz uzyskać dostęp do dowolnego elementu movieList. Napisz nazwę elementu, do którego chcesz uzyskać dostęp, tak jakby była właściwością obiektu movieList. Zauważ, że w ścieżce omijamy element główny : trace(movieList.listName); // wyświetli: Moje ulubione filmy
Dla elementów z więcej niż jednym węzłem tego samego rodzaju, jak np. , dodaj nawiasy kwadratowe z numerem indeksu, tak jakby wartość była przechowywana w tablicy. Pamiętaj, że pierwszy znacznik będzie posiadać indeks 0. trace(movieList.movie[1]); // wyświetli: Rushmore 1998 Wes Anderson
Zagłęb się bardziej w węzeł filmów poprzez użycie wielu operatorów kropki i nazw elementów: trace(movieList.movie[0].title); // wyświetli: Tytus trace(movieList.movie[2].director); // wyświetli: Woody Allen
Możesz też użyć tej składni do zapisywania w XML nowych wartości: movieList.movie[2].director = "Allen Konigsberg";
Metody służące do uzyskiwania dostępu do elementów potomnych Do tej pory przyglądałeś się sposobom uzyskiwania dostępu do danych poprzez użycie operatorów. Klasa XML oferuje również kilka metod służących do pobierania wartości. Używając metody child(), możesz pobrać kolekcję elementów potomnych o podanej nazwie. Poniższe dwa wiersze kodu dają identyczne wyniki: trace(movieList.movie); trace(movieList.child("movie"));
Używając child(), możesz przeszukać element w celu odnalezienia wszystkich elementów potomnych o danej nazwie. Możesz też użyć metody children() do pobrania dziecka na podstawie jego indeksu zamiast poprzez nazwę węzła. trace(movieList.children()[0].toXMLString()); // wyświetli: Moje ulubione filmy trace(movieList.children()[2].children()[0].toXMLString()); // wyświetli: Rushmore
Jeśli nie znasz indeksu danego węzła dziecka, możesz sprawdzić go za pomocą funkcji childIndex(). Na przykład:
Rozdział 10. Praca z XML
231
trace(movieList.movie[2].childIndex()); // wyświetli: 3
Jeśli użyjesz metody name(), otrzymasz nazwę węzła dziecka, do której chcesz uzyskać dostęp: trace(movieList.children()[1].name()); // wyświetli: movie
Ostatecznie, gdy musisz szukać tylko elementów, jednocześnie ignorując węzły tekstowe, komentarze i instrukcje przetwarzania, możesz użyć metody elements() zwracającej tylko elementy. W tym przykładzie metoda ta zwróci ten sam wynik, co metoda children().
Użycie operatora @ do uzyskania dostępu do atrybutów W celu uzyskania dostępu do atrybutów używa się narzędzia podobnego jak w przypadku dostępu do potomka, zwanego identyfikatorem atrybutu, który zapisywany jest w postaci znaku @ (at). Po znaku @ zapiszesz nazwę atrybutu, którego chcesz użyć: trace(movieList.movie[0].@id); // wyświetli: 123
Metoda ta może zostać wykorzystana nie tylko do odczytu wartości atrybutów, ale też do ich ustawienia: movieList.movie[0].@id = 8495309;
Jeśli chcesz uzyskać dostęp do wszystkich atrybutów znacznika, możesz użyć symbolu gwiazdki: var myXML:XML = ; trace(myXML.@*.toXMLString()); // wyświetli: 1 2 3
Tak jak operator kropki posiada odpowiadające sobie metody child() i children(), tak operator @ posiada metody attribute() oraz attributes(). Służą one do uzyskiwania dostępu do atrybutów odpowiednio na podstawie nazw lub listy XML. movieList.movie[1].attribute("id"); // 456 movieList.movie[2].attributes(); // 789
Powyższy kod jest funkcjonalnie identyczny z tym zamieszczonym poniżej: movieList.movie[1].@id; // 456 movieList.movie[2].@*; // 789
Dostęp do tekstu wewnątrz elementu Jeśli element zawiera jedynie węzeł tekstowy, metoda toString() zwróci tę wartość automatycznie. Ponieważ metoda toString() jest wywoływana automatycznie podczas użycia trace(), instrukcja ta stanowi szybki sposób wyświetlania wartości węzła tekstowego. Jednak niektóre elementy będą zawierać oprócz tekstu jeszcze inne węzły dzieci, jak np. ten przedstawiony poniżej:
232
Część II Praca z obiektami ActionScript 3.0
Węzeł tekstowy węzeł tekstowy węzła stuffNode Inny węzeł tekstowy
Aby w sposób jawny uzyskać dostęp do węzła tekstowego elementu, możesz użyć metody text(), zwracającej obiekt typu XMLList wypełniony węzłami tekstowymi: trace(movieList.movie[1].title.text()); // wyświetli: Rushmore
Uważaj, żeby nie pomylić toString() z toXMLString(). Podczas pracy z XML metoda toString() zwraca wartość węzła (który może być pusty) zamiast samego węzła, podczas gdy toXMLString() zwraca węzeł oraz zawarty w nim tekst. Metody te omówimy w podrozdziale „Konwertowanie na łańcuchy znaków i odwrotnie”.
Operator dostępu do potomków Bardzo potężną funkcją E4X jest zdolność do bezpośredniego uzyskiwania dostępu do węzłów potomków. Potomek jest dowolnym węzłem zawartym wewnątrz jakiegoś elementu lub w jednym z potomnych węzłów tego elementu, lub w ich węzłach potomnych itd. Innymi słowy, węzeł potomek jest dzieckiem elementu, jego wnukiem, prawnukiem, praprawnukiem itd. Poprzez użycie operatora dostępu do potomka, zapisywanego w postaci podwójnej kropki (..), możesz zanurzyć się głęboko w danych bez przejmowania się wyglądem ścieżki do tych danych. Działa to dla elementów, ale również dla atrybutów i innych typów obiektów XML. W zamieszczonym niżej przykładzie uzyskasz wszystkie tytuły filmów za jednym zamachem: trace(movieList..title);
Spowoduje to wyświetlenie następujących wyników: Tytus Rushmore Annie Hall
Prawda, że to proste? Podwójna kropka jest jeszcze bardziej wartościowa podczas pracy z większymi drzewami XML. Wypróbujmy ten operator na innym przykładzie i pobierzmy wszystkie atrybuty z całego dokumentu XML: trace(movieList..@*.toXMLString()); // wyświetli: 123 // 456 // 789
Wynik podobny do użycia tego operatora można uzyskać również przez wywołanie metody descendants(). Funkcja ta działa w ten sam sposób, co podwójna kropka: trace(movieList.descendants("year").text().toXMLString()); // wyświetli: 1999 // 1998 // 1977
Rozdział 10. Praca z XML
233
Jedną z rzeczy, jakich będziesz chciał uniknąć podczas używania potomków, jest zwracanie wszystkich wyników, nawet jeśli występują znaczniki o tej samej nazwie na innych poziomach drzewa. W następnym przykładzie występuje znacznik , który jest potomkiem innego znacznika : var foo:XML =
foo
; trace(foo..b.toXMLString());
Wyświetli to następujące wyniki:
foo
foo
Jak widzisz, foo jest zwracane dwukrotnie, raz jako potomek pierwszego znacznika i drugi raz osobno.
Dostęp do przodków Jeśli potomek jest węzłem lub atrybutem zawartym wewnątrz danego elementu, to przodek musi być węzłem zawierającym jakiś element. Wprawdzie nie ma operatora umożliwiającego dostęp do przodka, ale klasa XML posiada metodę parent(), zwracającą następny wyższy węzeł w łańcuchu dla danego obiektu XML: var title:XMLList = movieList.movie[1].title; var director:XMLList = title.parent().director; trace(title + " - reżyseria: " + director); // wyświetli: Rushmore – reżyseria: Wes Anderson
Iterowanie po dzieciach elementu Powiedzmy, że musisz przeprowadzić jakąś operację na każdym z kilku elementów XML. Możesz chcieć np. wypisać zawartość wszystkich elementów albo utworzyć listę nowych pozycji poprzez sortowanie swoich filmów według daty ukazania się ich. Dzięki E4X jest to bardzo łatwym zadaniem. Wiele z funkcji wspomnianych przez nas wcześniej, w tym operator dostępu do dziecka (.), zwraca obiekty typu XMLList. Jak wspomnieliśmy krótko wcześniej, obiekty XMLList działają podobnie jak wyspecjalizowane tablice przechowujące dane XML. Jak każda tablica posiadają rozmiar oraz indeksy. Można również po nich iterować jak po tablicy. Poniżej znajdziesz prosty przykład dodawania nazw filmów do tablicy i alfabetycznego porządkowania ich: var movieTitles:Array = new Array(); var movies:XMLList = movieList.movie; for (var i:int = 0; i < movies.length(); i++) {
234
Część II Praca z obiektami ActionScript 3.0 movieTitles[i] = movies[i].title; } movieTitles.sort(); trace(movieTitles); // wyświetli: Annie Hall,Rushmore,Tytus
Oprócz tego możesz użyć pętli for..in oraz for each..in do iterowania po obiekcie XMLList. Poniższy kod: var movies:XMLList = movieList.movie; for each (var movie:XML in movies) { movie.title = movie.title.toUpperCase(); } trace(movies.title);
wyświetli: TYTUS RUSHMORE ANNIE HALL
Filtrowanie wewnątrz XML E4X dodaje również potężną zdolność filtrowania danych „w locie” za pomocą nawiasowego operatora filtrowania XML (). Tworząc ścieżkę wewnątrz drzewa XML, możesz dodać wyszukiwanie w postaci wyrażenia boolowskiego, podobnego do tych używanych w instrukcji if. Będzie to miało na celu wyznaczenie wartości między nawiasami. W wyniku wyszukiwania zostanie zwrócony obiekt XMLList zawierający wszystkie węzły odpowiadające kryterium wyszukiwania. Wynikiem jest bardzo łatwe w obsłudze narzędzie natychmiastowego filtrowania. Przyjrzyjmy się kilku przykładom. Pierwszy z nich filtruje na podstawie węzła year filmu, drugi na podstawie atrybutu id: var classics:XMLList = movieList.movie.(year < 1990).title; trace(classics); // wyświetli: Annie Hall var idSort:XMLList = movieList.movie.(@id > 400).title; trace(idSort); // wyświetli: Rushmore // Annie Hall
Możesz filtrować według dowolnego kryterium, dopóki będziesz pamiętał o tym, że wyrażenia filtrujące są wyznaczane w zasięgu węzła, do którego je kierujesz. W tym przypadku wszystkie przykłady szukają wewnątrz obiektu typu XMLList, movieList.movie. Oto przykład użycia wyszukiwania łańcuchowego dla nazwiska reżysera: var julieMovies:XMLList = movieList.movie.(director.search("Julie") != -1).title; trace(julieMovies); // wyświetli: Tytus
Nie ma metody odpowiadającej operacji filtrowania, jak było w przypadku niektórych wcześniej omawianych operatorów.
Rozdział 10. Praca z XML
235
Konstruowanie obiektów XML Możesz pracować z predefiniowanymi wartościami XML, ale nie tylko. Klasa XML zawiera również metody służące do dodawania i usuwania danych. Już widziałeś, w jaki sposób możesz zmieniać wartości pojedynczych węzłów, a teraz pokażemy kilka sposobów łączenia w całość obiektów XML, co pozwoli na tworzenie bardziej złożonych obiektów.
Łączenie węzłów XML Najpierw utworzymy nowy element XML dla naszego czwartego filmu, który dodany zostanie do listy. var anotherMovie:XML = Tron 1982 Steven Lisberger ;
Ten element, anotherMovie, używa tego samego formatu, co elementy movie z movieList, z którymi pracowaliśmy do tej pory, ale nie jest częścią drzewa movieList. Przyjrzymy się kilku sposobom dodania tego filmu do naszej listy. Metody przeznaczone do łączenia węzłów XML powinny być dla Ciebie bardzo jasne.
Użycie operatorów + oraz += Możesz dodać kolejne dzieci do obiektu typu XMLList za pomocą operatorów + oraz +=, podobnie jak w przypadku łączenia łańcuchów znaków. Może się to okazać najprostszym sposobem dodawania danych do struktury XML. Poniższy kod: movieList.movie += anotherMovie; trace(movieList);
wyświetli:
Moje ulubione filmy
Tytus 1999 Julie Taymor
Rushmore 1998 Wes Anderson
Annie Hall 1977 Woody Allen
Tron
236
Część II Praca z obiektami ActionScript 3.0 1982 Steven Lisberger
Nowy film, Tron, został dodany na końcu zastawu elementów movie. Przykład ten wykorzystuje operator +=, który samodzielnie dokłada dane do zmiennej. Możesz użyć też operatora +. Oba wiersze zamieszczone poniżej dają dokładnie takie same rezultaty: movieList.movie += anotherMovie; movieList.movie = movieList.movie + anotherMovie;
Dodawane wartości niekoniecznie muszą posiadać ten sam format, co pozostałe elementy obiektu XML. Możesz użyć operatorów + oraz += do dodania węzłów do konkretnego elementu. W kolejnym przykładzie dodajemy znacznik do Annie Hall, a następnie dodajemy nowy tekst do tego elementu reprezentującego gatunek filmowy. Wykonanie kodu: var annieHall:XML = movieList.movie[2]; annieHall.children += ; annieHall.genre += "Komedia"; trace(annieHall);
wyświetli:
Annie Hall 1977 Woody Allen Komedia
Użycie metod XML do łączenia wartości Jeśli potrzebujesz większej kontroli nad określaniem dokładnego miejsca dodania swoich wartości w obiekcie XML, możesz użyć metod wbudowanych w klasę XML, służących do dodawania wartości do innych węzłów. Najpierw przyjrzymy się metodzie appendChild(), której działanie jest analogiczne z działaniem operatora +=. Możesz użyć appendChild() do dodania wartości na końcu obiektu XML lub obiektu XMLList. W poniższym przykładzie dodajemy znaczniki do wszystkich pozostałych filmów za pomocą metody appendChild(): movieList.movie[0].appendChild(Dramat); movieList.movie[1].appendChild(Komedia); movieList.movie[3].appendChild(Fantastycznonaukowy); trace(movieList.movie);
Ten kod da następujący wynik:
Tytus 1999 Julie Taymor Dramat
Rozdział 10. Praca z XML
237
Rushmore 1998 Wes Anderson Komedia
Annie Hall 1977 Woody Allen Komedia
Tron 1982 Steven Lisberger Fantastycznonaukowy
Zauważ, że w tym przykładzie jesteśmy w stanie dodać węzeł dziecka bezpośrednio do obiektu movie, bez uprzedniego uzyskiwania dostępu do children, jak robiliśmy w przykładzie z użyciem +=. Jeśli dodawanie na końcu nie jest tym, czego szukasz, są jeszcze inne sposoby dodawania wartości do obiektu XML. Należą do nich metoda prependChild(), dodająca wartości na początku obiektu, oraz insertChildAfter() i insertChildBefore(), które umożliwiają Ci określenie położenia punktu wstawiania do obiektu. Następny przykład demonstruje wszystkie te możliwości: var example:XML =
; example.appendChild(); trace(example); // // // // // // example.prependChild(); trace(example); // // // // // // // example.insertChildAfter(example.b, ); trace(example); // //
238
Część II Praca z obiektami ActionScript 3.0 // // // // // // example.insertChildBefore(example.f, ); trace(example); // // // // // // // // //
Usuwanie węzłów XML Nie istnieją metody służące do usuwania węzłów XML. Zamiast tego używa się po prostu operatora delete. Usuwa on konkretny element lub wartość z drzewa. Załóżmy, że męczą mnie już neurotyczne rozważania Woody’ego Allena i chcę usunąć go z mojej listy. Wykonanie kodu: delete movieList.movie[2]; trace(movieList.movie);
wyświetli:
Tytus 1999 Julie Taymor Dramat
Rushmore 1998 Wes Anderson Komedia
Tron 1982 Steven Lisberger Fantastyczno naukowy
Operator delete działa również dla innych typów węzłów, np. dla atrybutów. W poniższym przykładzie usuwamy wszystkie atrybuty filmu Tron. var tron:XML = movieList.movie[2]; delete tron.@*; trace(tron);
Rozdział 10. Praca z XML
239
// wyświetli: // Zauważ, że zniknął atrybut id.. // Tron // 1982 // Steven Lisberger // Fantastyczno naukowy // tron.id = 222; // Ponowne dodanie id.
Duplikowanie obiektu XML Jak już pisaliśmy we wcześniejszych rozdziałach, tylko proste wartości są przekazywane przez wartość, natomiast wszystkie złożone obiekty w ActionScript są przekazywane przez referencję. XML nie jest tutaj żadnym wyjątkiem. Takie ustawienie wartości, aby była równa innemu obiektowi XML, spowoduje utworzenie referencji do oryginalnego obiektu i wszystkie zmiany dokonane na jednej z tych dwóch zmiennych będą miały wpływ na obie zmienne, co czasami może dawać niechciane rezultaty. var template:XML = ; var me:XML = template; me.name.first = "Mims"; me.name.last = "Wright"; trace(template); // wyświetli: // // Mims // Wright // //
Dlatego też istnieje metoda copy() tworząca duplikat oryginalnego obiektu XML, dzięki czemu może on zostać przekazany przez wartość, a nie przez referencję. Pozwoli Ci to na bezpieczne dokonywanie zmian w nowej kopii danych XML bez zmiany oryginalnych danych. var template:XML = ; var me:XML = template.copy(); me.name.first = "Mims"; me.name.last = "Wright"; trace(template); // wyświetli: // // // // // trace(me); // wyświetli: // // Mims // Wright // //
240
Część II Praca z obiektami ActionScript 3.0
Zamiana wartości w węzłach XML Węzły dzieci obiektu XML mogą zostać zastąpione wszystkie naraz za pomocą metody setChildren(). Zastępuje ona wszystkie węzły dzieci podanym przez Ciebie XML. W następnym przykładzie zamienimy film Tron najpierw na pusty element, a następnie na film Jak we śnie: movieList.movie.(@id == 222).setChildren(null); trace(movieList.movie.(@id == 222).toXMLString()); // wyświetli: null movieList.movie.(@id == 222).setChildren(Jak we śnie + 2006 + Michel Gondry + Komedia romantyczna); trace(movieList.movie.(@id == 222).toXMLString()); // wyświetli: // Jak we śnie // 2006 // Michel Gondry // Komedia romantyczna //
Podobna metoda o nazwie replace() pozwala na zastąpienie pojedynczego węzła nowym obiektem XML. Ten nowy obiekt XML może mieć dowolną postać i nie musi używać tej samej nazwy znacznika, co element zastępowany: movieList.movie.(@id == 222).replace("genre", Niezależny); trace(movieList.movie.(@id == 222).toXMLString()); // wyświetli: // Jak we śnie // 2006 // Michel Gondry // Niezależny //
Więcej informacji na temat metod setChildren() oraz replace() znajdziesz w dokumentacji AS3.
Konwertowanie na łańcuchy znaków i odwrotnie Obiekty XML składają się w głównej mierze z danych tekstowych. Z tego też powodu najczęściej będziesz miał do czynienia z konwersją łańcuchów znaków na XML oraz z XML na łańcuchy znaków. Operacje te są bardzo proste, ale oferują dodatkową funkcjonalność, wykraczającą poza możliwości standardowej metody toString().
Konwertowanie łańcuchów znaków na XML Czasami możesz znaleźć się w sytuacji, w której będziesz musiał przekonwertować łańcuchy znaków na XML, np. podczas pracy z danymi pochodzącymi ze źródła bazującego na łańcuchach znaków lub z zewnętrznego pliku tekstowego. Aby tego dokonać, możesz po
Rozdział 10. Praca z XML
241
prostu użyć konstruktora XML do rzutowania tekstu jako danych XML. Bądź ostrożny i używaj podczas rzutowania jedynie poprawnie skonstruowanego tekstu XML. Jeśli nie zachowasz ostrożności, to możesz spotkać się z wystąpieniem błędów czasu wykonywania. var dialog:String = "Lorem ipsum dolor sit amet"; var XMLString:String = "" + dialog + ""; var xml:XML = XML(XMLString); trace(xml.text()); // wyświetli: Lorem ipsum dolor sit amet
Konwertowanie XML na łańcuchy znaków Jak już mogłeś się przekonać w tym rozdziale, możesz używać tradycyjnej metody toString() dla obiektów XML w celu wyświetlania ich zawartości w postaci łańcuchów znaków. Klasa XML oferuje jednak również metodę toXMLString(), działającą trochę inaczej. Metoda toString() dla obiektów XML wyświetla zazwyczaj zawartość elementu XML razem z wszelkimi znacznikami. Jeśli dany obiekt XML składa się tylko z jednego węzła, np. bar — zwrócona zostanie jedynie wartość tekstowa tego węzła, w tym przypadku "bar". Z kolei metoda toXMLString() zawsze zwraca pełny znacznik XML oraz wartości zawarte w elemencie. W tym przypadku metoda toXMLString() zwróciłaby: "bar". var meal:XML = Kolacja
Sałata
Ziemniaki Stek
; trace(meal.name); // wyświetli: Kolacja trace(meal.course[0]); // wyświetli: // Sałata // trace(meal.course[1].item[0].toXMLString()); // wyświetli: Ziemniaki trace(meal.course[1].item[1].toString()); // wyświetli: Stek trace(meal.course[1].item[1][email protected]()); // wyświetli: Średnio dopieczony trace(meal..item.toString()); // wyświetli: Sałata // Ziemniaki // Stek
Formatowanie kodu Masz kilka możliwości formatowania łańcucha XML utworzonego przez wywołania metod toString() oraz toXMLString(). Pierwsza z nich to flaga prettyPrinting. Gdy jest ustawiona, łańcuchy XML są automatycznie normalizowane poprzez usunięcie pustych znaczników oraz nadmiernych białych znaków. Flaga ta dotyczy wszystkich obiektów XML i jest domyślnie ustawiona na wartość true:
242
Część II Praca z obiektami ActionScript 3.0 var example:XML =
; XML.prettyPrinting = false; trace(example); // wyświetli: XML.prettyPrinting = true; trace(example); // wyświetli: // // // // //
Zwróć uwagę na automatycznie przeprowadzone porządkowanie, które usunęło większość białych znaków. Dzięki włączeniu prettyPrinting kod XML posiada lepszą strukturę.
Ustawienie liczby spacji we wcięciu Po zastosowaniu rozwiązania prettyPrinting tekst zawiera automatyczne wcięcia. Druga statyczna właściwość, jaką możesz ustawić, to prettyIndent. Jest to liczba całkowita określająca liczbę spacji używanych dla każdego poziomu wcięć. Domyślna wartość to 2. Ustawienie to jest ignorowane, gdy flaga prettyPrinting ustawiona jest na false. Poniższy kod: XML.prettyIndent = 0; trace(example); XML.prettyIndent = 8; trace(example);
Wyświetli tekst bez żadnej spacji dla wcięcia:
a następnie z wcięciami składającymi się z ośmiu spacji:
Normalizacja węzłów tekstowych Obiekty XML dopuszczają użycie wielu węzłów tekstowych w elemencie, jednak w większości przypadków używa się tylko jednego węzła tekstowego na element. Jeśli wystąpi jednak rzadki przypadek i będziesz miał więcej niż jeden węzeł tekstowy w danym elemen-
Rozdział 10. Praca z XML
243
cie, możesz użyć metody normalize() do usunięcia białych znaków i połączenia tych węzłów tekstowych w jeden, ciągły węzeł. W następnym przykładzie dodajemy do naszej listy czwartego błazna za pomocą serii wywołań appendChild(). // dodaj "shemp" za pomocą trzech wywołań appendChild() example.appendChild("sh"); example.appendChild(""); example.appendChild("emp"); trace(example.text().length()); // wyświetli: 3 trace(example.text().toXMLString()); // wyświetli: sh // // emp
Zauważ, że długość obiektu XMLList zwróconego przez text() wynosi 3 i trzy linie zostaną wyprowadzone podczas wyświetlania tekstu. Normalizacja spowoduje scalenie tych trzech węzłów tekstowych w jeden. example.normalize(); trace(example.text().length()); // wyświetli: 1 trace(example.text().toXMLString()); // wyświetli: shemp
Pamiętaj o tym, że w większości przypadków węzły tekstowe zawierające biały znak będą istnieć z tym znakiem jako pojedynczy węzeł tekstowy. Tylko w sytuacjach, w których umyślnie dodanych zostało wiele węzłów tekstowych, jak w poprzednim przykładzie, metoda normalize() zostanie użyta.
Wczytywanie danych XML z zewnętrznych źródeł Wczytywanie danych z serwerów jest tematem na tyle obszernym, że nie da się go omówić w tym rozdziale na przykładzie XML. Z tego też powodu poświęcamy mu osobny podrozdział w dalszej części książki. Jeśli jednak nie możesz się doczekać i już teraz chciałbyś wczytać jakieś dane z serwera, to przedstawiamy tutaj podstawowe informacje na ten temat. Implementacja XML w AS2 cechowała się tym, że dane były wczytywane poprzez metodę wywoływaną przez klasę XML. W AS3 ta funkcja została wyciągnięta na zewnątrz klasy — do pakietu flash.net. W tym przykładzie użyjemy obiektu URLLoader do wczytania pliku XML, a następnie utworzenia obiektu XML z wczytanych danych. import flash.net.*; var myXML:XML; var url:URLRequest = new URLRequest("http://www.twoja-domena.pl/test.xml"); var loader:URLLoader = new URLLoader(url); loader.addEventListener(Event.COMPLETE, onLoadComplete); function onLoadComplete(event:Event):void { if (loader.data) { myXML = XML(loader.data); } }
244
Część II Praca z obiektami ActionScript 3.0
Możesz zastąpić adres URL z tego przykładu lokalizacją swoich danych. Gdy dane zostaną wczytane, wywoływana jest funkcja onLoadComplete(), konwertująca wczytane dane na format XML. Od tej chwili możesz używać myXML tak jak innych danych XML z tego rozdziału.
Gromadzenie metadanych węzłów XML Klasa XML umożliwia sprawdzenie niektórych metadanych węzłów XML, z którymi pracujesz. Może to być przydatne podczas automatyzowania przetwarzania obiektów XML. Możesz wykorzystać metody do odnajdywania typu węzła oraz typu zawartości przechowywanej przez węzeł, zarówno tej prostej, jak i bardziej złożonej.
Odnajdywanie typów węzłów Do określenia typu węzła, z którym pracujesz, możesz użyć metody nodeKind() dla badanej gałęzi. Funkcja ta zwraca łańcuch znaków opisujący rodzaj węzła. Wartość łańcucha będzie równa jednej z tych: element, attribute, text, comment, processing-instruction.
Jeśli chcesz pracować z komentarzami i instrukcjami przetwarzania, musisz wyłączyć odpowiednio flagi ignoreComments oraz ignoreProcessingInstructions. Więcej informacji na ten temat znajdziesz w dalszej części tego rozdziału.
Poniższy przykład wykorzystuje kod XHTML: XML.ignoreComments = false; XML.ignoreProcessingInstructions = false; var XHTMLExample:XML =
Witaj!
; trace(XHTMLExample.body.nodeKind()); // wyświetli: element trace([email protected]()); // wyświetli: attribute trace(XHTMLExample.body.text().nodeKind()); // wyświetli: text trace(XHTMLExample.body.comments()[0].nodeKind()); // wyświetli: comment trace(XHTMLExample.body.processingInstructions().nodeKind()); // wyświetli: // processing-instruction
Rozdział 10. Praca z XML
245
Określanie typu zawartości w węźle O węzłach zawierających jedynie pojedynczy węzeł tekstowy lub wcale nieposiadających zawartości mówi się, że posiadają prostą zawartość. O węzłach zawierających inne węzły mówi się, że posiadają złożoną zawartość. Aby określić, czy węzeł zawiera prostą, czy złożoną zawartość, możesz wywołać metodę hasComplexContent() lub hasSimpleContent(). Obie metody dadzą poprawny wynik — użyj tej, która wyda Ci się bardziej odpowiednia. var contentTest:XML =
Lorem Ipsum
; trace(contentTest.a.hasSimpleContent()); // wyświetli: true trace(contentTest.b.hasSimpleContent()); // wyświetli: true trace(contentTest.c.hasSimpleContent()); // wyświetli: false trace(contentTest.d.hasComplexContent()); // wyświetli: false
Używanie przestrzeni nazw Przestrzenie nazw są konwencją służącą w XML do pomocy w grupowaniu elementów i atrybutów w zestawy o podobnej funkcjonalności. Sposób ten jest bardzo podobny do grupowania zestawów klas w pakiety. Pliki XML będą używać deklaracji przestrzeni nazw, definiującej nazwę dla przestrzeni nazw oraz unikalny identyfikator służący do odróżniania jej od pozostałych przestrzeni nazw (może być czymkolwiek, ale zazwyczaj jest to jakiegoś rodzaju URI):
URI przestrzeni nazw XML nie musi mieć niczego wspólnego ze stroną zamieszczoną pod tym adresem. Musi jedynie stanowić unikalny łańcuch znaków. Ogólnie rzecz biorąc, adresy URI uznaje się za jedyne w swoim rodzaju, ale takie jest również zdanie „Dziusiei idu na riba”, które także nadaje się do tego celu.
Aby zdefiniować węzeł jako członka konkretnej przestrzeni nazw, użyj nazwy tej przestrzeni nazw, a po niej dwukropka i nazwy swojego elementu lub atrybutu.
Praca z XML
Miej na uwadze to, że nawet jeśli pracowałeś z XML w przeszłości, możesz nie znać się na przestrzeniach nazw (lub — co bardziej prawdopodobne — pracowałeś z nimi, nie będąc tego świadomym). Oto kilka znaczników z przestrzeniami nazw, które być może znasz z kanałów RSS, wywołań SOAP czy z Adobe Flex:
246
Część II Praca z obiektami ActionScript 3.0
Ze względu na obszerność zagadnienia używania przestrzeni nazw nie opisujemy go w tej książce. Jeśli jednak jesteś zainteresowany szczegółami działania przestrzeni nazw, możesz zapoznać się z tutorialem W3C dostępnym pod adresem www.w3schools.com/xml/xml_ namespaces.asp. Przestrzenie nazw XML oraz klasa Namespace są zupełnie czym innym niż słowo kluczowe namespace używane w konstrukcji klas w celu umożliwienia specyficznego dostępu do właściwości lub metod klasy. Więcej informacji na temat takiej przestrzeni nazw oraz słowa kluczowego namespace znajdziesz w rozdziale 3.
Praca z przestrzeniami nazw w ActionScript Praca z przestrzeniami nazw w E4X jest dość skomplikowana, gdy porówna się ją ze stosowaniem operatorów kropki oraz @ używanych do uzyskiwania dostępu do elementów oraz atrybutów. Najpierw przyjrzyjmy się samej klasie Namespace. Klasa ta reprezentuje przestrzeń nazw XML, która zasadniczo jest tylko identyfikatorem przestrzeni nazw lub przedrostkiem (np. rss, soap, mx), oraz URI przestrzeni nazw (np. http://helion.pl). To już wszystko, czym zajmuje się ta klasa. Utworzenie przestrzeni nazw jest proste i może zostać zrealizowane poprzez przekazanie przedrostka oraz URI lub samego URI do konstruktora przestrzeni nazw. Poniższy kod: var fooNamespace:Namespace = new Namespace("http://bar.com");
lub var fooNamespace:Namespace = new Namespace("foo", "http://www.bar.com");
dadzą ten sam wynik, czyli przestrzeń nazw o nazwie fooNamespace, ale pierwsza będzie posiadać przedrostek o wartości null, podczas gdy druga będzie posiadać przedrostek "foo". O ile nie potrzebujesz przedrostka, URI zawiera wystarczająco dużo informacji, aby umożliwić pracę z tą przestrzenią nazw. Obiekty typu Namespace zwracają swoje wartości URI po użyciu metody toString(): trace(fooNamespace.toString()); // wyświetli: http://www.bar.com
Przestrzenie nazw można także tworzyć za pomocą metody namespace() obiektu XML. Metoda ta pobiera jeden parametr w postaci łańcucha znaków, który jest przedrostkiem jednej z przestrzeni nazw zdefiniowanych w Twoim XML. Przyjrzyj się następującym przykładowym danym: var example:XML = Lorem ipsum
W tym przykładzie tworzymy drzewo XML definiujące przestrzeń nazw z przedrostkiem test oraz z URI http://foo.com. Teraz użyjemy metody namespace() do uzyskania dostępu do tych danych: var test:Namespace = example.namespace("test"); trace(test.prefix); // wyświetli: test trace(test.uri); //wyświetli: http://foo.com
Rozdział 10. Praca z XML
247
Metoda namespace() będzie próbować zwrócić przestrzeń nazw dla dowolnego przedrostka przekazanego jej jako parametr. Jeśli przekazany przedrostek jest niepoprawny, metoda ta zwróci undefined. Jeśli pominiesz wszystkie parametry naraz, metoda zwróci domyślną przestrzeń nazw zdefiniowaną dla tego elementu. Alternatywnie możesz też użyć metody namespaceDeclarations() do zwrócenia tablicy wszystkich przestrzeni nazw zdefiniowanych dla danego elementu. var example:XML = trace(example.namespaceDeclarations()[1]); // wyświetli: http://mimswright.com
Metoda inScopeNamespaces() działa podobnie jak namespaceDeclarations(). Więcej informacji na ten temat znajdziesz w dokumentacji języka ActionScript 3.0.
Użycie operatora :: E4X definiuje inny operator służący do uzyskiwania dostępu do węzłów z przestrzeniami nazw: podwójny dwukropek (::). Możesz używać go w parze z obiektem przestrzeni nazw do uzyskania dostępu do obiektów posiadających inną przestrzeń nazw od domyślnej. Pokażemy to na naszym oryginalnym przykładzie XML: var example:XML = Lorem Ipsum
; var test:Namespace = example.namespace("test"); trace(example.test::element[0]); // wyświetli: Lorem Ipsum
Podwójny dwukropek powoduje, że ActionScript uzyska dostęp do węzła element z wnętrza przestrzeni nazw „test”. Tej samej składni można użyć do uzyskania dostępu do atrybutów zdefiniowanych z wykorzystaniem przestrzeni nazw: trace(example.test::element[1].@test::attribute); // wyświetli: Dolor Sit Amet
Pracując od czasu do czasu z jedną przestrzenią nazw, będziesz chciał ustawić domyślną przestrzeń nazw, zamiast każdorazowo pisać tę samą nazwę przestrzeni nazw. Możesz dokonać tego globalnie poprzez użycie frazy kluczowej default xml namespace (te trzy słowa działają razem jako jedna instrukcja): default xml namespace = test;
Zachowaj w tym przypadku ostrożność, ponieważ spowoduje to zmianę domyślnej przestrzeni nazw dla wszystkich nowych obiektów XML.
Idąc jeszcze dalej Poniżej znajdziesz kilka dodatkowych metod przeznaczonych do obsługi przestrzeni nazw XML, ale chcąc zachować prostotę przekazu, nie opisujemy ich szczegółowo w tej książce. Więcej informacji na ich temat znajdziesz w dokumentacji języka ActionScript 3.0.
248
Część II Praca z obiektami ActionScript 3.0
oraz removeNamespace() — pozwalają na dodawanie przestrzeni nazw do obiektów podczas dynamicznego tworzenia XML.
addNamespace() setNamespace()
— ustawia przestrzeń nazw obiektu XML na podaną przez Ciebie.
localName() — podobnie jak metoda name() zwraca nazwę węzła dla elementu, ale
w tym przypadku pomijany jest przedrostek przestrzeni nazw i zwracana jest jedynie lokalna nazwa. Np. .localName() zwróciłaby tylko "bar". setLocalName() — ustawia lokalną nazwę elementu z dołączoną przestrzenią nazw.
Praca z komentarzami i instrukcjami przetwarzania Pomimo iż nie są powszechnie używane w ActionScript, bloki komentarzy XML oraz instrukcje przetwarzania dołączone w Twoim kodzie są uznawane za węzły i z tego też powodu są dołączane do Twojego obiektu XML podczas parsowania danych. Jeśli będziesz musiał uzyskać dostęp do tych wartości, możesz użyć metody comments() lub metody processingInstructions() w celu uzyskania obiektu XMLList odpowiednio z komentarzami lub instrukcjami przetwarzania. Pamiętaj o tym, że statyczne właściwości ignoreComments oraz ignoreProcessingInstruc tions posiadają domyślnie wartość true i że musisz im przypisać wartość false, jeśli chcesz, aby Twoje metody comments() oraz processingInstructions() mogły działać poprawnie. Flagi te omówimy w dalszej części tego rozdziału. XML.ignoreComments = false; XML.ignoreProcessingInstructions = false; var XHTMLExample:XML =
Witam!
; trace(XHTMLExample.*.comments().length()); // wyświetli: 2 trace(XHTMLExample.body.comments().toXMLString()); // wyświetli: trace(XHTMLExample.body.comments()[1].toXMLString()); // wyświetli:
Przykład użycia klasy ExternalInterface
Musisz zaktualizować swoją wersję odtwarzacza Flash Player
Rozdział 36. Komunikacja z JavaScript
707
Podsumowanie
Możesz użyć klasy ExternalInterface do utworzenia zintegrowanych aplikacji, w których Flash i JavaScript komunikują się w sposób synchroniczny.
Używaj metody ExternalInterface.call() do wywoływania funkcji JavaScript z wnętrza zawartości Flash.
Używaj metody ExternalInterface.addCallback() w celu zarejestrowania funkcji ActionScript lub metody, tak aby mogła zostać wywołana z poziomu JavaScript.
708
Część X Wdrażanie programu
Rozdział 37.
Wykorzystanie połączeń lokalnych do komunikacji pomiędzy filmami Flash W tym rozdziale:
Umożliwianie komunikacji pomiędzy aplikacjami Flash w lokalnej domenie
Umożliwianie komunikacji pomiędzy aplikacjami Flash z różnych domen
Klasa flash.net.LocalConnection umożliwia dowolnej aplikacji Flash w dowolnym programie odtwarzającym komunikowanie się z inną aplikacją Flash w innym programie odtwarzającym na tym samym komputerze bez potrzeby stosowania złożonych skryptów JavaScript lub innych obejść. Wymagane jest tylko to, aby obie aplikacje były uruchomione na tym samym komputerze i żeby jedna z nich była skonfigurowana do wysyłania wiadomości, a druga do ich nasłuchiwania. Jako przykład zastosowania LocalConnection wyobraź sobie aplikację wdrożoną w internecie, składającą się z kilku plików SWF, które muszą ze sobą współpracować. Dzięki LocalConnection pliki SWF mogą się ze sobą komunikować. Jest wiele możliwości wykorzystania LocalConnection. W tym rozdziale poznasz sposób pracy z tą klasą.
Tworzenie aplikacji wysyłającej Z komunikacją za pomocą klasy LocalConnection wiążą się zasadniczo dwa typy aplikacji. Pierwszą z nich jest aplikacja wysyłająca. Wysyłanie może zostać zrealizowane w zaledwie dwóch krokach. Pierwszym z nich jest oczywiście utworzenie obiektu Local Connection. Konstruktor LocalConnection nie wymaga żadnego parametru, zatem możesz utworzyć go w następujący sposób: var sender:LocalConnection = new LocalConnection();
Po utworzeniu obiektu musisz jedynie wywołać metodę send() w celu komunikacji z aplikacją odbierającą. Metoda send() wymaga minimum dwóch parametrów: nazwy połączenia, którego chcesz użyć do wysyłania, oraz nazwy metody wywoływanej w aplikacji
710
Część X Wdrażanie programu
odbierającej. Nazwa połączenia jest dowolna i zależna od Ciebie, ale musi odpowiadać nazwie połączenia nasłuchującego w aplikacji odbierającej. Oto prosty przykład, w którym obiekt LocalConnection rozgłasza wiadomość za pomocą połączenia o nazwie aConnection. Metoda wywoływana nazwana została someMethod: var sender:LocalConnection = new LocalConnection(); sender.send("aConnection", "someMethod");
Wysyłanie parametrów Możesz wywołać metodę jak wyżej lub też przesłać parametry do metody aplikacji odbierającej. Wszelkie parametry dodane do metody send() po dwóch wymaganych (nazwie połączenia i nazwie metody) są przesyłane do metody aplikacji odbierającej. Poniższy przykład pokazuje sposób przekazania do metody someMethod trzech parametrów: var sender:LocalConnection = new LocalConnection(); sender.send("aConnection", "someMethod", true, "dwa", 3);
Oprócz prostych typów danych jako parametry przesyłane mogą być obiekty i tablice: var sender:LocalConnection var objectParameter:Object var arrayParameter:Array = sender.send("aConnection",
= new LocalConnection(); = {a: "jeden", b: "dwa", c: "trzy"}; [1,2,3,4,5]; "someMethod", objectParameter, arrayParameter);
Sprawdzanie statusu wysyłania Każdemu wywołaniu metody send() towarzyszy zdarzenie wyzwalane przez obiekt. Jeśli wysłanie nie powiodło się z powodu ograniczeń obszaru izolowanego, obiekt LocalConne ction generuje zdarzenie securityError. Z tego powodu, jeśli chcesz, aby aplikacja poprawnie obsługiwała błędy zabezpieczeń dla lokalnych połączeń, powinieneś zarejestrować obiekt nasłuchujący dla zdarzeń bezpieczeństwa tego typu: sender.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
Jednak w większości przypadków obiekt LocalConnection będzie generował zdarzenia statusu. Zdarzenie to posiada właściwość level, która będzie miała wartość status lub error. Jeśli wartość będzie równa status, to wywołanie zakończyło się powodzeniem i aplikacja odbierająca obsłużyła żądanie. Wartość error oznacza, że aplikacja odbierająca odrzuciła żądanie. Jeśli musisz zapewnić poprawny odbiór żądania, to powinieneś zarejestrować obiekt nasłuchujący dla zdarzenia statusu. Zdarzenie to jest typu Status Event.STATUS: sender.addEventListener(StatusEvent.STATUS, statusHandler);
Tworzenie aplikacji odbierającej Aplikacja odbierająca jest odrobinę bardziej skomplikowana od aplikacji wysyłającej. Najprostsza aplikacja odbierająca wymaga wykonania czterech kroków:
Rozdział 37. Wykorzystanie połączeń lokalnych do komunikacji
711
1. Utwórz nowy obiekt LocalConnection. 2. Zdefiniuj metodę, która zostanie wywołana przez aplikację wysyłającą. 3. Wskaż obiektowi LocalConnection, który obiekt powinien zostać użyty do obsługi tych żądań. 4. Poinstruuj aplikację, aby nasłuchiwała wiadomości dla danej nazwy połączenia. Pierwszy krok odpowiada pierwszemu krokowi aplikacji wysyłającej, tylko zamiast nazwy sender używamy tutaj receiver: var receiver:LocalConnection = new LocalConnection();
Drugi krok po prostu definiuje metodę, która zostanie wywołana przez aplikację wysyłającą: public function nazwaMetody():void { // tutaj wpisz kod }
Następnie musisz poinformować obiekt LocalConnection, jaki obiekt powinien obsługiwać żądania. Obiekt obsługujący żądania nazywany jest klientem i możesz po prostu przypisać referencję do właściwości client obiektu LocalConnection. Np. jeśli zdefiniujesz metodę tak, aby została wywołana wewnątrz tej samej klasy, w której zdefiniowałeś obiekt LocalConnection, przypisz referencję do tego obiektu (this) do właściwości client obiektu LocalConnection. receiver.client = this;
Ostatni krok wykonywany jest przez metodę connect()wywoływaną przez utworzony obiekt LocalConnection. Przekazujesz do niej nazwę połączenia, na którym aplikacja powinna nasłuchiwać: receiver.connect(nazwaPolaczenia);
Nazwa metody musi odpowiadać nazwie metody przekazywanej jako drugi parametr metody send() w aplikacji wysyłającej. Nazwa połączenia dla metody connect() musi odpowiadać nazwie połączenia przekazanej jako pierwszy parametr metody send() w aplikacji wysyłającej. Aplikacja odbierająca po wywołaniu metody connect() nasłuchuje na połączeniu, dopóki jej nie każesz przestać. Możesz zakończyć połączenie poprzez wywołanie metody close() na obiekcie LocalConnection. Na przykład: receiver.close();
Wysyłanie i odbieranie pomiędzy domenami Obiekty LocalConnection domyślnie próbują komunikować się z tą samą domeną. Oznacza to, że gdy aplikacja wysyłająca jest uruchomiona pod adresem www.rightaction script.com, domyślnie wysyła dane do innych aplikacji uruchomionych pod adresem
712
Część X Wdrażanie programu www.rightactionscript.com. Wprowadzając tylko kilka zmian, możesz skonfigurować aplikacje wysyłające i odbierające wiadomości pomiędzy domenami, tak że aplikacja uruchomiona np. pod adresem www.rightactionscript.com będzie mogła wysyłać dane do aplikacji w domenie www.themakers.com.
Aplikacja wysyłająca Aplikacja wysyłająca wymaga tylko jednej modyfikacji w celu wysyłania do innych domen. Pierwszy parametr metody send() (nazwa połączenia) może być modyfikowany na jeden z dwóch sposobów. Możesz dodać do nazwy przedrostek w postaci nazwy domeny, do której ma zostać wysłane polecenie, lub użyć nazwy połączenia rozpoczynającej się od podkreślenia. Oba sposoby działają podobnie, ale nieznacznie się różnią. Jeśli nazwa połączenia nie rozpoczyna się od nazwy domeny lub podkreślenia, Flash automatycznie konwertuje nazwę połączenia na localDomain:connection. Domeną jest zawsze superdomena — domena bez uwzględniania poddomeny, jak np. www. Jeśli np. poniższy kod zostanie wykonany z www.rightactionscript.com, Flash automatycznie przekonwertuje nazwę połączenia na rightactionscript.com:aConnection. sender.send("aConnection", "someMethod");
Jeśli wiesz, że chcesz wysłać wywołanie do aplikacji Flash działającej w domenie the makers.com, możesz w przedrostku nazwy połączenia użyć themakers.com, w następujący sposób: sender.send("themakers.com:aConnection", "someMethod");
Powyższy wiersz kodu działa dobrze, gdy chcesz wysyłać tylko do aplikacji Flash działających w konkretnej domenie. Jeśli jednak chcesz wysyłać wywołania do aplikacji działających w dowolnych domenach, to kod ten nie zadziała. W takich sytuacjach możesz użyć nazwy połączenia rozpoczynającej się od podkreślenia. Gdy nazwa połączenia rozpoczyna się podkreśleniem, Flash nie tworzy przedrostka z nazwy domeny. Poniższy fragment kodu będzie wysyłał dane do wszystkich aplikacji Flash z dowolnych domen, o ile tylko nasłuchują one połączenia _aConnection: sender.send("_aConnection", "someMethod");
Aplikacja odbierająca Aplikacja odbierająca również wymaga kilku modyfikacji, jeśli chcesz, aby odbierała wywołania z aplikacji wysyłających działających w innych domenach. Nie możesz poprzedzić nazwy połączenia w aplikacji odbierającej inną domeną, tak jak to było możliwe w aplikacji wysyłającej. O ile nazwa połączenia nie rozpoczyna się podkreśleniem, Flash automatycznie dodaje domenę aplikacji odbierającej do nazwy połączenia i nie możesz tego zmienić. Jeżeli aplikacja odbierająca jest uruchomiona np. pod adresem www.rightaction script.com, poniższe połączenie jest interpretowane przez Flash jako rightactionscript. com:aConnection: receiver.connect("aConnection");
Rozdział 37. Wykorzystanie połączeń lokalnych do komunikacji
713
Dopóki aplikacja wysyłająca również działa pod adresem www.rightactionscript.com lub posiada nazwę połączenia rightactionscript.com:aConnection, wywołania będą przechwytywane przez aplikację odbierającą. Jeśli aplikacja wysyłająca używa nazwy połączenia rozpoczynającej się od podkreślenia, aplikacja odbierająca musi używać tej samej nazwy połączenia z podkreśleniem. Jednak samo odebranie wywołania przez aplikację odbierającą nie oznacza, że zrobi z nim to, co kazała jej zrobić aplikacja wysyłająca. Domyślnie bowiem aplikacja może odbierać wywołania, ale akceptuje tylko wywołania z domeny lokalnej. Możesz jednak akceptować inne domeny poprzez użycie metody allowDomain(). Gdy aplikacja odbierająca otrzymuje dane z aplikacji wysyłającej, Flash Player musi zdecydować, czy zaakceptować to żądanie, czy nie. Czyni to poprzez konsultację z obiektem LocalConnection, sprawdzając, czy wysyłająca domena znajduje się na liście dozwolonych domen. Żądania pomiędzy domenami są domyślnie zabronione. Jednak możesz za pomocą metody allowDomain() w aplikacji odbierającej określić jedną lub więcej domen, z których żądania będą akceptowane. Poniższy przykład akceptuje wszystkie żądania z rightaction script.com , www.rightactionscript.com, themakers.com oraz www.themakers.com: receiver.allowDomain("rightactionscript.com", "www.rightactionscript.com", "themakers.com", "www.themakers.com");
Możesz też użyć znaku * jako wieloznacznika, oznaczającego wszystkie domeny: receiver.allowDomain("*");
Jeśli odbierający plik SWF jest obsługiwany przez zabezpieczony serwer używający protokołu HTTPS, a serwer wysyłający używa standardowego HTTP, to żądanie nie zostanie akceptowane, nawet jeśli użyta zostanie metoda allowDomain(). Zamiast niej musisz w takiej sytuacji użyć metody allowInsecureDomain(). Metoda allowInsecureDomain() działa tak samo jak metoda allowDomain(), ale dodatkowo zezwala na żądania pochodzące z HTTP, gdy odbierający plik SWF obsługiwany jest przez HTTPS.
Podsumowanie
Aplikacje Flash mogą komunikować się z innymi aplikacjami Flash za pomocą klasy LocalConnection. Klasa LocalConnection umożliwia aplikacji wysyłającej wywołanie metody w aplikacji odbierającej.
Domyślnie obiekt LocalConnection zezwala tylko na połączenia w obrębie tej samej domeny. Jednak po wprowadzeniu kilku zmian w aplikacjach wysyłającej i odbierającej komunikacja może odbywać się pomiędzy różnymi domenami.
714
Część X Wdrażanie programu
Skorowidz -, 64, 185 --, 64, 185 !, 67 !=, 67 #include, 91 $iinit(), 160 %, 64, 185 %=, 65 &&, 67 (), 234 *, 63, 64, 92, 185 *=, 65 ., 227 .., 232 ..., 145, 222 /, 64, 185 /* */, 77 //, 77 /=, 65 ::, 112, 247 ?:, 70 @, 227, 231 [], 197, 227 [Embed], 644 _blank, 348 _parent, 348, 463 _playPosition, 526 _root, 286 _self, 348, 463 _top, 348, 463 ||, 67 +, 63, 64, 185, 235 ++, 63, 64, 185 +=, 65, 235