Za każdym razem gdy wykonywana jest funkcja, w niewidoczny sposób
tworzony jest obiekt. Obiekt ten przechowuje wszystkie zmienne lokalne
zadeklarowane słowem kluczowym var, przekazane do funkcji parametry,
oraz tablicę arguments. Obiekt ten nazywany jest obiektem aktywacji.
Termin "obiekt aktywacji" także pojawia się tylko w jednym
miejscu słownika ActionScript, i znowu ma to miejsce przy opisie with.
Gdy funkcja się wykonuje, jako bieżący łańcuch zasięgu używany jest
łańcuch funkcji, a obiekt aktywacji znajduje się na jego szczycie.
Zatem wewnątrz funkcji podczas wykonywania skryptu, łańcuch zasięgu
wygląda następująco:
obiekt aktywacji -> łańcuch zasięgu funkcji
W powyższym kodzie, jeśli funkcja była utworzona w pierwszej klatce
_root, to łańcuch zasięgu podczas jej wykonywania wygląda tak:
obiekt aktywacji -> _root -> _global
Jeśli chodzi o zarządzanie pamięcią, pojęcie obiektu aktywacji rzuca
również nieco światła na to co dzieje się podczas wykonywania funkcji:
1. tworzony jest obiekt aktywacji
2. wszystkie zmienne lokalne tworzone są jako właściwości obiektu aktywacji
3. kod wykonywany jest w kontekście obiektu, którym jest obiekt aktywacji
4. gdy kod się kończy, obiekt aktywacji odkładany jest na stos śmieci i zasoby zostają zwolnione. Jest tak dlatego, bo nigdzie już nie ma odwołania do obiektu aktywacji.
Funkcje zagnieżdżone i marnowanie pamięci
Wstęp
Dotarliśmy wreszcie do interesującej części (przepraszam
że to tyle zajęło). We flashu MX nowy model zdarzeń czyni niezwykle
łatwym przypisywanie w locie funkcji do uchwytów zdarzeń (lub do innej
zmiennej). Logiczną rzeczą która przychodzi do głowy jest utworzenie
funkcji jako opakowania które ma za zadanie wykonanie konkretnej operacji
ORAZ przypisywać różne funkcje do uchwytów zdarzeń
Na przykład:
1 |
resetMC = function(mc) { |
Funkcja "resetMC" pobiera movie clip jako parametr, resetuje jego położenie do (0,0) I przypisuje funkcję to uchwytu zdarzenia onEnterFrame co powoduje ruch klipu diagonalnie w kierunku prawego dolnego rogu. To brzmi rozsądnie, ale jest tu haczyk.
Trwały obiekt aktywacji
Zanim przejdziemy dalej, musimy wprowadzić nieco prostej
terminologii, w przeciwnym razie, trudno będzie pisać o tym co się
dzieje. Po prostu, gdy funkcja jest tworzona wewnątrz innej funkcji,
funkcję pierwszą nazywamy wewnętrzną, a drugą zewnętrzną.
Jeśli więc zrozumiałeś wszystko co zostało napisane dotychczas, to
wiesz pewnie o co chodzi. W powyższym kodzie za każdym razem gdy wywoływana
jest funkcja "resetMC" tworzony jest obiekt aktywacji i
dodawany jest on do łańcucha zasięgu funkcji, tworząc bieżący łańcuch
zasięgu. Gdy zostanie osiągnięta linia 3, tworzona jest funkcja wewnętrzna
i przypisywana jako uchwyt do zdarzenia onEnterFrame movie clipu przekazanego
jako parametr.
Gdy utworzona jest funkcja wewnętrzna, bieżący łańcuch zasięgu zostanie
przyporządkowany tej funkcji jako jej własny łańcuch zasięgu. Chodzi
o to, że obiekt aktywacji funkcji zewnętrznej jest częścią bieżącego
łańcucha zasięgu, co oznacza że w łańcuchu zasięgu funkcji wewnętrznej
jest teraz trzymana referencja do niego.
Jeśli powyższy kod zostałby napisany w pierwszej klatce _root, łańcuch
zasięgu powiązany z funkcją wewnętrzną wyglądałby tak:
obiekt aktywacji funkcji zewnętrznej -> _root -> _global
a gdy następnie funkcja zostanie wykonana łańcuch zasięgu będzie taki:
obiekt aktywacji funkcji wewnętrznej -> obiekt aktywacji funkcji
zewnętrznej -> _root -> _global
" No i co z tego?" zapytasz. To, że ponieważ referencja
do obiektu aktywacji funkcji zewnętrznej jest częścią łańcucha zasięgu
funkcji wewnętrznej, a funkcja wewnętrzna jako metoda movie clipu
jest trwała, zatem sam obiekt aktywacji staje się obiektem trwałym.
W rzeczy samej dla garbage collectora istnieje teraz referencja do
obiektu aktywacji więc nie może on zostać usunięty. Z tego powodu,
obiekt aktywacji wraz ze wszystkimi zmiennymi lokalnymi które zawiera,
nie może zostać zwolniony z pamięci.
Duplikacja funkcji
Jest kilka konsekwencji tworzenia funkcji wewnątrz funkcji.
Po pierwsze, jak wspomnieliśmy wcześniej za każdym razem gdy wykonywana
jest funkcja, tworzony jest nowy obiekt aktywacji. Zatem, nawiązując
do poprzedniego przykładu, wyobraź sobie że chcemy 100 razy wywołać
funkcję resetMC w celu przypisania uchwytu onEnterFrame do 100 movie
clipów. Czyniąc to w ten sposób spowodujemy utworzenie 100 różnych
obiektów aktywacji, które będą zajmowały pamięć.
Po drugie, ponieważ funkcja wewnętrzna jest tworzona i przypisywana
w funkcji zewnętrznej, to każdy movie clip będzie miał przypisany
różny obiekt funkcyjny, każdy zajmujący pewien obszar pamięci. Jest
to nadzwyczaj nieefektywne rozwiązanie, bo funkcja wewnętrzna robi
to samo z każdym movie clipem to którego została przypisana (kod jest
taki sam). Przy okazji, to także powód dla którego OOP zaleca stosowanie
metod klas dostępnych w prototypie zamiast tworzenia ich w konstruktorze.
Gdyby były one tworzone w konstruktorze, mielibyśmy duplikację funkcji
dla każdej instancji klasy.
Marnowanie pamięci, nie wyciek pamięci
Rozróżnienie tych dwóch pojęć jest dosyć istotne. Na
wyciek pamięci natrafiamy gdy program stale zwiększa zasoby z których
korzysta, prowadząc czasem do zawieszenia systemu.
W przypadku zagnieżdżonych funkcji we flashu nie mamy wycieku pamięci,
ale marnowanie pamięci. Funkcja wewnętrzna przechowuje referencję
do obiektu aktywacji funkcji zewnętrznej, ale jest to relacja jeden-do-jednego.
Jeżeli wewnętrzna funkcja zostanie usunięta z pamięci (na przykład
gdy obiekt do którego została przypisana funkcja wewnętrzna został
usunięty), to unikatowa referencja do obiektu aktywacji jest również
kasowana i garbage collector skasuje zarazem (powinien skasować) funkcję
wewnętrzną jak i obiekt aktywacji funkcji zewnętrznej. W ten sposób
zasoby użyte na przechowanie obiektu aktywacji funkcji zewnętrznej
zostały zmarnowane, ale się nie zwiększają, a co za tym idzie użycie
pamięci nie wzrasta w sposób niekontrolowany.
Czy możemy to udowodnić?
O trwałości obiektu aktywacji
Oczywiście! Rozważ ten kawałek kodu:
1 |
test = function(obj) { |
W funkcji "test", "a" jest zmienną
lokalną, więc możemy oczekiwać, że po wywołaniu funkcji "test",
"a" jest niszczone, a pamięć zwolniona. Jednakże, gdy uruchomimy
powyższy kod wywołanie "meth" na obiekcie "o"
da wynik "5", co oznacza, że "a" ZOSTAŁO znalezione
w łańcuchu zasięgu "meth" i nie zostało zwolnione z pamięci.
Mógłbyś pomyśleć, że "a" zostało odnalezione, bo w funkcji
wewnętrznej była jawnie użyta referencja do niej. Flash mógł wykonać
"coś" wewnętrznie aby utrzymać przy życiu tę referencję.
Było by miło, ale w rzeczywistości nie w tym rzecz. Obiekt aktywacji
funkcji zewnętrznej BYŁ przechowany w łańcuchu zasięgo funkcji wewnętrznej
i dlatego mamy z niej dostęp do wszystkich zmiennych lokalnych.
Używając eval, możemy dynamicznie poszukać zmiennej w łańcuchu zasięgu.
Popatrz na ten kod:
1 |
test = function(obj) { |
W powyższym kodzie, funkcja "retrieve" nie zawiera jawnie wpisanej referencji do żadnej zmiennej, ale przekazując nazwę zmiennej i używając eval, możemy uzyskać dostęp do wszystkich zmiennych lokalnych, o których myśleliśmy że zostały skasowane. Mimo że zmienne te nie były używane w programie, to pozostawały w pamięci, marnując zasoby.
O duplikacji i marnowaniu pamięci
I znów! Rozważmy taki kod:
1 |
addFunc = function(obj) { |
Linia 19 pokazuje, że pomimo tej samej nazwy, metody
obiektów "o1" i "o2" nie odnoszą się do tego samego
obiektu funkcji w pamięci.
Linia 20 pokazuje, że pomimo tego, że dla obu obiektów "o1"
i "o2" zwracany przez "theFunc" obiekt ma tą samą
wartość właściwości "txt", to same zwracane obiekty różnią
się, co oznacza, że łańcuch "hello there" jest przechowywany
w pamięci w dwóch miejscach, po jednym dla każdego obiektu.
Zatem w rzeczywistości za każdym gdy wywołujemy "addFunc"
w celu dodania metody "theFunc" do obiektu, łańcych "Hello
there" jest duplikowany w pamięci.
Jak temu zaradzić
Jeśli szczególnie zainteresowała cię wspomniana wyżej
cecha funkcji zagnieżdżonych, to najlepszym rozwiązaniem jest utworzenie
wszystkich twoich funkcji na jednych poziomie, a następnie zamiast
zagnieżdżania używać wewnątrz funkcji jedynie referencji do nich.
Na przykład jeśli przepiszemy powyższy kod, będzie to wyglądało tak:
1 |
theFunc = function() { |
Jak widać z powyższego kodu, polecenia trace w liniach
17 i 18 pokazują undefined. To dlatego, bo gdy wywoływana jest "theFunc"
na obiektach o1 i o2, Flash nie może znaleźć referencji do "aVariable"
w łańcuchu zasięgu, co oznacza, że obiekt aktywacji funkcji "addFunc"
nie został dodany do łańcucha zasięgu metod i zmienna lokalna "aVariable"
została usunięta tak jak powinniśmy tego oczekiwać.
Co więcej, linia 20 wypisuje teraz "true", co oznacza, że
obiekt funkcji zarówno dla obiektu "o1" oraz "o2"
jest jednakowy i zajmuje to samo miejsce w pamięci.
Powyższe podejście przyspieszy również działanie kodu jeśli chodzi
o pamięć. Zasadniczo utworzenie nowej funkcji w pamięci zajmuje chwilę
czasu: czas na przydział pamięci, czas na transfer danych itd. Gdy
więc używasz funkcji zagnieżdzonych, to z każdym wywołaniem funkcji
zewnętrznej zajmujesz kilka cykli procesora na zbudowanie funkcji
wewnętrznej. Tworzenie funkcji z góry a następnie wykonanie prostego
przypisania w funkcji zewnętrznej sprawi że wykonanie kodu zajmie
mniej czasu.
Wnioski
Oto podsumowanie rozdziału poświęconego łańcuchowi zasięgu
i marnotrawieniu pamięci. Reasumując wypunktujmy to co do tej pory
zostało powiedziane:
1. Łańcuch zasięgu to sekwencja obiektów, które Flash przeszukuje w poszukiwaniu zmiennej.
2. Gdy funkcja jest tworzona, związywany jest z nią bieżący łańcuch zasięgu.
3. Za każdym razem gdy funkcja jest wykonywana tworzony jest nowy obiekt, zwany obiektem aktywacji, który przechowuje wszystkie zmienne lokalne, a następnie jest on umieszczany na szczycie związanego z funkcją łańcucha zasięgu.
4. Gdy zagnieżdżamy funkcje, obiekt aktywacji funkcji zewnętrznej zostaje umieszczony w łańcuchu zasięgu funkcji wewnętrznej.
5. Jeśli funkcja zostaje powiązana do obiektu trwałego jako jego metoda lub jest zwracana przez funkcję zewnętrzną, to wtedy funkcja wewnętrzna staje się trwała, a wraz z nią obiekt aktywacji funkcji zewnętrznej. To prowadzi do marnotrawienia pamięci.
6. Aby uniknąć marnowania pamięci, prostym rozwiązaniem jest nie używanie funkcji zagnieżdżonych, ale tworzenie funkcji zewnętrznie, a następnie przypisywanie referencji do nich.
To wszystko jest rzeczywiści proste. Problem w tym że wcale nie jest
oczywiste. Zapewne wiele razy marnowałeś pamięć nie wiedząc nawet
o tym. Zanim uciekniesz aby modyfikować swoje skrypty w których użyłeś
funkcji zagnieżdżonych, chciałbym zwrócić uwagę, że w większości przypadków
to wszystko nie ma wielkiego znaczenia. W małych projektach mało prawdopodobne
jest że wyczerpiesz wszystkie zasoby maszyny i jeśli projekt sprawuje
się dobrze, po prostu się tym nie martw. To o czym tu napisałem ma
znaczenie przy wielkich projektach a pomyślałem że dobrze jest wiedzieć
co Flash robi za Twoimi plecami. Myślę że największe marnotrawienie
pamięci ma miejsce przy operowaniu dużymi łańcuchami tekstu i funkcjami
zagnieżdżonymi. Łańcuch może osiągać długość kilkudziesięciu lub nawet
kilkuset znaków. Jeśli więc używasz długich łańcuchów jako zmiennych
lokalnych i stają się one trwałe, to możesz mieć poważny problem.
Jeśli jest to kilka liczb całkowitych lub referencji, to nie powinno
się stać nic złego.
Tylko nie każ mi mówić tego o czym nie powiedziałem, bo jeśli o mnie
chodzi to im czystszy plik w sensie rozmiaru, zużycia pamięci i wydajności,
tym lepiej. Zatem pamiętaj o tym gdy piszesz kody w ActionScripcie
:).
Dziękuje że dotarłeś aż do tego momentu. Mam nadzieję że to, co tu
przeczytałeś przyda Ci się. Jeśli zauważyłeś jakąś nieścisłość czy
błąd (kod, fakty, terminologię, gramatykę, pisownię itd.) lub jeśli
uważasz że inne przykłady objaśniające temat byłyby lepsze lub jeśli
po prostu chciałbyś podzielić się innymi informacjami nie krępuj się
napisać (
Zanim przejdę dalej, pomyślałem że ten rozdział mógł nasunąć kilka
pytań więc poniżej znajduje się sekcja "dodatki".
I ostatnia notka: cecha zawsze staje się źródłem eksperymentów. Funkcje
zagnieżdżone mogą prowadzić to marnowania pamięci, ale jest to korzystne
w niektórych aplikacjach. Na przykład prywatne i statyczne właściwości
mogą być zaimplementowane używając cechy łańcucha zasięgu. Więcej
na ten temat tu.
Odnośniki
Większość tego co znajduje się tutaj zostało zaczerpnięte
z różnych wątków listy FlashCoders. Najciekawszym wątkiem według mnie
jest:
http://chattyfig.figleaf.com/ezmlm/ezmlm-cgi?1:sss:56601:200212:blejmgjoemfcdojimbmn#b
Dodatki - pytania/odpowiedzi
Jak głęboko może sięgać łańcuch zasięgu?
Nie mam zielonego pojęcia. Próbowałem zagnieździć 5 poziomów funkcji i dla nich wszystkich obiekt aktywacji był trwały.
1 |
a1 = 5; |
Ponieważ możemy odczytać wszystkie zmienne oznacza to,
że obiekt aktywacji każdej funkcji znajdował się w łańcuchu zasięgu
funkcji najbardziej wewnętrznej.
Tak więc, naprawdę nie wiem jak głęboko może sięgać łańcuch zasięgu,
ale prawdopodobnie dosyć głęboko. W każdym razie powyższy kod ma 5
poziomów zagnieżdżenia i jeśli twój kod w rzeczywistym projekcie wygląda
tak jak ten, to myślę że powinieneś poważnie jeszcze raz przeanalizować
swoją strategię kodowania :).
Łańcuch zasięgu i łańcuch prototypów
Wspomnieliśmy wcześniej że gdy Flash szuka zmiennej
robi to wzdłuż łańcucha zasięgu. To prawda, a na jego szczycie istnieje
jeszcze jeden mechanizm, którego Flash używa poszukując zmiennej.
Ten mechanizm to łańcuch dziedziczenia znany także jako łańcuch prototypów.
Ponieważ ten artykuł nie jest o OOP, nie chcę się za bardzo wgłębiać
więc odeślę Cię do książki online Roberta Debreuil'a, która jest najlepszym
źródłem wiedzy na temat OOP we Flashu.
W skrócie, każdy obiekt we Flashu jest instancją pewnej klasy, która
to może dziedziczyć po jeszcze innej klasie i tak dalej. Każda klasa
może mieć swój własny zestaw właściwości i metod. Kiedy wywołujesz
metodę na obiekcie, to jeśli metoda nie jest zdefiniowana dla tego
właśnie obiektu to Flash będzie przeszukiwał łańcuch dziedziczenia
aby znaleźć szukaną metodę wyżej w hierarchii klas.
Pomimo, że łańcuch zasięgu używany jest wewnętrznie, to łańcuch dziedziczenia
jest dostępny dla programistów, a to co łączy obiekt z łańcuchem prototypów
jest referencja "__proto__". Gdy ma miejsce przeszukiwanie
łańcucha zasięgu, uruchamiane jest także przeszukiwanie łańcucha prototypów
dla każdego obiektu w łańcuchu zasięgu.
Ilustruje to poniższy kod:
1 |
a = 5; |
Wartość "6" nie została przypisana zmiennej
"a" tak jak ma to miejsce w przypadku gdy jest ona zmienną
lokalną wewnątrz funkcji "addFunc". Mimo to została ona
wyświetlona jako wartość poszukiwanej zmiennej "a". Po kolei
omówmy więc, co Flash zrobił gdy wykonywana była metoda "meth"?
Na początku Flash szukał "a" w obiekcie aktywacji "meth"
ale jej tam nie znalazł, następnie sprawdził czy obiekt aktywacji
metody "meth" ma referencję __proto__. Ponieważ jej nie
ma (albo jej nie widzimy), Flash poszedł dalej przeszukując następny
obiekt w łańcuchu zasięgu którym jest obiekt aktywacji funkcji "addFunc".
Tam też jej nie znalazł. Flash więc sprawdził czy istnieje referencja
__proto__ tego obiektu; okazało się że istnieje. Flash zajrzał czy
obiekt wskazywany przez referencję __proto__ zawiera "a"
i ją tam znalazł, po czym ją wyświetlił.
Jak przypisywane są zmienne bez jawnie podanego zasięgu ?
Nie wiem czy powinienem o tym mówić na samym początku.
Po raz kolejny spotykamy właściwość która nie jest skomplikowana,
ale nie jest też oczywista. Gdy tworzysz przypisanie "myVar=5",
każdy z obiektów łańcucha zasięgu jest przeszukiwany w celu znalezienia
"myVar" POZA _global. Jeżeli choć jeden z tych obiektów
(nazwijmy go "o") posiada właściwość "myVar" to
przypisanie zostanie dokonane na "o". Jeśli żaden z nich
nie posiada tej właściwości, to tworzona jest zmienna "myVar"
dla najniżej położonego obiektu w hierarchii łańcucha zasięgu. Ten
obiekt będzie się znajdował zaraz nad _global.
Najwidoczniej w przypadku przypisania łańcuch prototypów nie jest
przeszukiwany, więc nawet jeśli "myVar" istnieje gdzieś
w łańcuchu prototypów obiektu "o", to przypisanie nie będzie
wykonane na "o" jeśli nie jest on ostatnim obiektem w łańcuchu
zasięgu tuż nad _global.
Aby stworzyć albo ustawić właściwość obiektu _global, _global musi
zostać podane jawnie.
Poniżej znajduje się kilka skryptów testowych, które to ilustrują:
1 |
o1 = {}; |
1 |
o1 = {a:4}; |
1 |
o1 = {}; |
With i łańcuch zasięgu
Kocham "with" i nienawidzę "with". To dlatego że ponieważ ma wpływ na łańcuch zasięgu upodabnia go do funkcji zagnieżdżonych. Na przykład to o czym mówiliśmy powyżej na temat przypisania jest prawdą zarówna dla funkcji zagnieżdżonych jak i dla "with". Jest jednak kilka różnic. Największa o której chcę powiedzieć jest związana z tworzeniem funkcji. Wspominałem wcześniej w tym artykule że do funkcji dociągany jest bieżący łańcuch zasięgu gdy jest ona tworzona. Jak widzieliśmy powoduje to utrwalenie obiektu aktywacji. Jeśli do funkcji jest dociągany obiekt aktywacji w momencie jej tworzenia, to oznacza to że możemy użyć "with" w celu dodania obiektu do łańcucha zasięgu funkcji. To jednak nie działa, spójrz na ten przykład:
1 |
o1 = {a:5}; |
W powyższym kodzie użyliśmy "with" w celu umieszczenia obiektu "o1" na szczycie łańcucha zasięgu. Linia 4 pokazuje że możemy odnieść się do "a" tak jak powinno być. Mając bieżący łańcuch zasięgu tworzymy funkcję (linia 5) i przypisujemy ją do "o2" jako jego metodę. Potem wywołujemy "o2.aMethod();", co zwraca nam undefined. To oznacza, że "a" nie zostało znalezione w łańcuchu zasięgu i oznacza że o1 nie zostało dodane do łańcuchu zasięgu funkcji. Obiekt ręcznie wstawiony do łańcucha zasięgu za pomocą "with" NIE JEST uważany za część łańcucha zasięgu dociąganego do funkcji w czasie jej tworzenia.
Co z referencją this ?
Na ile udało mi się stwierdzić łańcuch zasięgu nie ma
wpływu na "this" bez względu na głębokość. "this"
zawsze wskazuje na obiekt na którego rzecz wywoływana jest funkcja.
Inaczej mówiąc, gdy łańcuch zasięgu funkcji nigdy się nie zmienia,
to znaczenie "this" wewnątrz funkcji zależy od tego na jakim
obiekcie wywoływana jest ta funkcja. Jeśli wrócimy do jednego z pierwszych
przykładów:
1 |
a = 5; |
Stwierdzenie, że "'this' zawsze odnosi się do obiektu z którego funkcja została wywołana", prowadzi do interesującej cechy. Wspomnieliśmy wcześniej że zmienne lokalne są po prostu właściwościami obiektu aktywacji. Jeśli używając var zdefiniujemy funkcję dla obiektu aktywacji i uruchomimy ją z wnętrza obiektu aktywacji to "this" wewnątrz tej funkcji będzie wskazywało właśnie na obiekt aktywacji! Wykorzystując ten fakt możemy uzyskać referencję do obiektu aktywacji i ją sobie gdzieś przechować (jeśli widzisz z tego jakiś użytek) lub użyć operatora tablicy aby utworzyć lub odczytać jakąś właściwość obiektu aktywacji (znów, jeśli coś takiego Ci się przyda).
1 |
a = 4; |
Operator tablicy i eval
Ludzie zawsze się zastanawiają czy eval w actionscripcie
wychodzi z użycia i jest niezalecane czy nie i jaka jest różnica pomiędzy
eval a operatorem tablicy. Cytat wypowiedzi Ralpha Bokelberga dostarcza
odpowiedzi na to pytanie tutaj - http://chattyfig.figleaf.com/ezmlm/ezmlm-cgi/1/67735
- cytat poniżej:
<quote>
Do you know the mantra of eval:
eval is not deprecated
eval is not useless
eval is just fine to use
how else would you access objects given by a targetstring
obj = {subobj: {prop: 666}}
path = "obj.subobj.prop";
trace(this[path]); //undefined
trace(eval(path)); //666
bokel
</quote>
Więc rzeczywiście jedną z głównych różnic jest to że eval ma dostęp
do właściwości obiektu mając daną ścieżkę, natomiast operator tablicy
ma dostęp jedynie bezpośredni.
Stosując to co zostało omówione, możemy pokazać różnicę pomiędzy eval
i operatorem tablicy wykazując ich związek z łańcuchem zasięgu i łańcuchem
prototypów. Zakładając że do pobrania pojedynczej właściwości (bez
ścieżki) używamy eval lub operatora tablicy możemy powiedzieć: operator
tablicy służy do pobrania zmiennej (właściwości) przeszukując łańcuch
prototypów danego obiektu; eval jest używane do wydobycia zmiennej
przeszukując bieżący łańcuch zasięgu. I, jak wspomnieliśmy wyżej przeszukiwanie
łańcucha zasięgu powoduje przeszukanie łańcucha prototypów dla każdego
obiektu w łańcuchu zasięgu.
Czy wszystko zrozumiałeś ?
Dobra, to już naprawdę koniec. Korzystając z wiedzy zdobytej w tym artykule i nie używając Flasha MX czy możesz powiedzieć co zostanie wyświetlone jako wynik działania tego kodu ?
1 |
getMethod = function() { |
a. 4
b. 5
c. 6
d. 7