Co to jest krótkie wprowadzenie do zakresu leksykalnego?
javascript
scoping
lexical-scope
Subba Rao
źródło
źródło
Odpowiedzi:
Rozumiem je poprzez przykłady. :)
Po pierwsze, zakres leksykalny (zwany również zakresem statycznym ), w składni podobnej do C:
Każdy poziom wewnętrzny może uzyskać dostęp do swoich zewnętrznych poziomów.
Istnieje inny sposób, zwany zakresem dynamicznym, używany przez pierwszą implementację Lisp , ponownie w składni podobnej do C:
Tu
fun
można albo dostępx
wdummy1
lubdummy2
lub dowolnyx
w dowolnej funkcji rozmowyfun
zx
zadeklarowanym w nim.wydrukuje 5,
wydrukuje 10.
Pierwszy nazywa się statyczny, ponieważ można go wywnioskować w czasie kompilacji, a drugi nazywa się dynamiczny, ponieważ zakres zewnętrzny jest dynamiczny i zależy od wywołania łańcucha funkcji.
Uważam, że statyczne ustalanie zakresu jest łatwiejsze dla oka. W końcu większość języków poszła w ten sposób, nawet Lisp (może zrobić oba, prawda?). Dynamiczne określanie zakresu jest jak przekazywanie referencji wszystkich zmiennych do wywoływanej funkcji.
Jako przykład tego, dlaczego kompilator nie może wydedukować zewnętrznego zakresu dynamicznego funkcji, rozważmy nasz ostatni przykład. Jeśli napiszemy coś takiego:
Łańcuch połączeń zależy od warunku czasu wykonywania. Jeśli to prawda, łańcuch połączeń wygląda następująco:
Jeśli warunek jest fałszywy:
Zewnętrzny zakres
fun
w obu przypadkach to osoba dzwoniąca plus osoba dzwoniąca i tak dalej .Wystarczy wspomnieć, że język C nie pozwala na zagnieżdżanie funkcji ani dynamiczne określanie zakresu.
źródło
JavaScript
. Dlatego uważam, że nie należy tego oznaczać jako przyjętej odpowiedzi. Zakres leksykalny szczególnie w JS jest innyfor
typowym problemem jest pętla. Zakres leksykalny JavaScript jest tylko na poziomie funkcji, chyba że używany jest ES6let
lubconst
.Spróbujmy najkrótszej możliwej definicji:
Schemat leksykalny określa, w jaki sposób nazwy zmiennych są rozwiązywane w funkcjach zagnieżdżonych: funkcje wewnętrzne zawierają zakres funkcji nadrzędnych, nawet jeśli funkcja nadrzędna powróciła .
To wszystko!
źródło
Powyższy kod zwróci „Jestem tylko lokalnym”. Nie zwróci „Jestem globalny”. Ponieważ funkcja func () liczy, gdzie została pierwotnie zdefiniowana, co jest objęte zakresem funkcji whatismyscope.
Nie będzie to przeszkadzało, niezależnie od tego, jak się nazywa (zakres globalny / nawet z innej funkcji), dlatego wartość zakresu globalnego, którym jestem globalny, nie zostanie wydrukowana.
Nazywa się to zasięgiem leksykalnym, w którym „ funkcje są wykonywane przy użyciu łańcucha zasięgu, który obowiązywał podczas ich definiowania ” - zgodnie z Przewodnikiem po definicji JavaScript.
Zakres leksykalny jest bardzo potężną koncepcją.
Mam nadzieję że to pomoże..:)
źródło
Określanie zakresu leksykalnego (AKA static) odnosi się do określania zakresu zmiennej wyłącznie na podstawie jej pozycji w tekstowym korpusie kodu. Zmienna zawsze odnosi się do środowiska najwyższego poziomu. Dobrze jest to zrozumieć w odniesieniu do zakresu dynamicznego.
źródło
Zakres określa obszar, w którym dostępne są funkcje, zmienne i tym podobne. Na przykład dostępność zmiennej jest zdefiniowana w jej kontekście, powiedzmy funkcja, plik lub obiekt, w którym są zdefiniowane. Zwykle nazywamy te zmienne lokalne.
Część leksykalna oznacza, że zakres można odczytać z odczytu kodu źródłowego.
Zakres leksykalny jest również znany jako zakres statyczny.
Zakres dynamiczny definiuje zmienne globalne, które można wywoływać lub do których można się odwoływać z dowolnego miejsca po zdefiniowaniu. Czasami nazywane są zmiennymi globalnymi, nawet jeśli zmienne globalne w większości języków programowania mają zakres leksykalny. Oznacza to, że można odczytać kod, że zmienna jest dostępna w tym kontekście. Być może trzeba zastosować klauzulę use lub include, aby znaleźć instancję lub definicję, ale kod / kompilator wie o zmiennej w tym miejscu.
Natomiast w dynamicznym określaniu zakresu najpierw wyszukujesz w funkcji lokalnej, a następnie w funkcji, która wywołała funkcję lokalną, a następnie w funkcji, która wywołała tę funkcję i tak dalej, w górę stosu wywołań. „Dynamiczny” odnosi się do zmiany, polegającej na tym, że stos wywołań może być różny za każdym razem, gdy wywoływana jest dana funkcja, a zatem funkcja może uderzać w różne zmienne w zależności od tego, skąd została wywołana. (patrz tutaj )
Aby zobaczyć ciekawy przykład zakresu dynamicznego, zobacz tutaj .
Więcej informacji można znaleźć tutaj i tutaj .
Kilka przykładów w Delphi / Object Pascal
Delphi ma zakres leksykalny.
Najbardziej zbliżone do zakresu dynamicznego Delphi to para funkcji RegisterClass () / GetClass (). Aby zobaczyć jego użycie, zobacz tutaj .
Powiedzmy, że czas wywołania RegisterClass ([TmyClass]) w celu zarejestrowania określonej klasy nie może być przewidziany przez odczytanie kodu (jest wywoływany w metodzie kliknięcia przycisku wywoływanej przez użytkownika), kod wywołujący GetClass („TmyClass”) otrzyma wynik czy nie. Wywołanie funkcji RegisterClass () nie musi znajdować się w zakresie leksykalnym jednostki za pomocą GetClass ();
Inną możliwością dla zakresu dynamicznego są metody anonimowe (zamknięcia) w Delphi 2009, ponieważ znają zmienne swojej funkcji wywołującej. Nie podąża stamtąd rekurencyjnie ścieżką wywoływania, a zatem nie jest w pełni dynamiczna.
źródło
Uwielbiam w pełni funkcjonalne, niezależne od języka odpowiedzi od ludzi takich jak @Arak. Ponieważ to pytanie zostało oznaczone JavaScript , chciałbym zagłębić się w notatki bardzo specyficzne dla tego języka.
W JavaScript wybieramy zakres:
var _this = this; function callback(){ console.log(_this); }
callback.bind(this)
Myślę, że warto zauważyć, że JavaScript nie ma tak naprawdę dynamicznego zakresu .
.bind
dostosowujethis
słowo kluczowe i jest blisko, ale technicznie nie jest takie samo.Oto przykład demonstrujący oba podejścia. Robisz to za każdym razem, gdy podejmujesz decyzję o sposobie określania zakresu wywołań zwrotnych, więc dotyczy to obietnic, procedur obsługi zdarzeń i innych.
Leksykalny
Oto, co możesz
Lexical Scoping
nazwać zwrotnymi w JavaScript:Uwiązany
Innym sposobem na zasięg jest użycie
Function.prototype.bind
:O ile mi wiadomo, metody te są równoważne behawioralnie.
źródło
bind
nie wpływa na zakres.Zakres leksykalny: Zmienne zadeklarowane poza funkcją są zmiennymi globalnymi i są widoczne wszędzie w programie JavaScript. Zmienne zadeklarowane w funkcji mają zasięg funkcji i są widoczne tylko dla kodu pojawiającego się w tej funkcji.
źródło
IBM definiuje to jako:
Przykład 1:
Przykład 2:
źródło
Zakres leksykalny oznacza, że w zagnieżdżonej grupie funkcji funkcje wewnętrzne mają dostęp do zmiennych i innych zasobów ich zakresu nadrzędnego . Oznacza to, że funkcje potomne są leksykalnie powiązane z kontekstem wykonania ich rodziców. Zakres leksykalny jest czasem określany również jako zakres statyczny .
Rzeczą, którą zauważysz w zakresie leksykalnym, jest to, że działa on naprzód, co oznacza, że nazwa jest dostępna w kontekstach wykonawczych jego dzieci. Ale nie działa wstecz na swoich rodziców, co oznacza, że rodzice
likes
nie mogą uzyskać dostępu do zmiennej .Mówi nam to również, że zmienne o tej samej nazwie w różnych kontekstach wykonywania zyskują pierwszeństwo od góry do dołu stosu wykonania. Zmienna o nazwie podobnej do innej zmiennej w najbardziej wewnętrznej funkcji (najwyższy kontekst stosu wykonawczego) będzie miała wyższy priorytet.
Pamiętaj, że jest to pobierane stąd .
źródło
W prostym języku zakres leksykalny jest zmienną zdefiniowaną poza twoim zakresem lub górny zakres jest automatycznie dostępny w twoim zasięgu, co oznacza, że nie musisz go tam przekazywać.
Przykład:
// Dane wyjściowe: JavaScript
źródło
bind
. Dzięki nimbind
nie jest już wymagany. Aby uzyskać więcej informacji o tej zmianie, sprawdź stackoverflow.com/a/34361380/11127383Brakuje ważnej części rozmowy dotyczącej zakresu leksykalnego i dynamicznego : proste wyjaśnienie okresu istnienia zmiennej o zasięgu lub kiedy można uzyskać do niej dostęp.
Dynamiczne określanie zakresu bardzo luźno odpowiada zakresowi określanemu „globalnie” w sposób, w jaki tradycyjnie o nim myślimy (powodem, dla którego przywołuję porównanie między nimi, jest to, że zostało już wspomniane - i nie podoba mi się szczególnie wyjaśnienie powiązanego artykułu ); prawdopodobnie najlepiej nie porównujemy globalnego i dynamicznego - choć podobno, zgodnie z powiązanym artykułem, „... [to] jest przydatne jako substytut zmiennych o zasięgu globalnym”.
Więc, w prostym języku angielskim, jakie jest ważne rozróżnienie między tymi dwoma mechanizmami określania zakresu?
Zakres leksykalny został bardzo dobrze zdefiniowany we wszystkich powyższych odpowiedziach: zmienne o zasięgu leksykalnym są dostępne - lub dostępne - na poziomie lokalnym funkcji, w której zostały zdefiniowane.
Jednak - ponieważ nie jest to cel PO - dynamiczne ustalanie zakresu nie spotkało się z dużym zainteresowaniem, a uwaga, którą otrzymała oznacza, że prawdopodobnie potrzebuje trochę więcej (nie jest to krytyka innych odpowiedzi, ale raczej „och, ta odpowiedź sprawiła, że żałujemy, że nie było trochę więcej ”). Oto trochę więcej:
Dynamiczne określanie zakresu oznacza, że zmienna jest dostępna dla większego programu przez cały czas trwania wywołania funkcji - lub podczas wykonywania funkcji. Naprawdę Wikipedia naprawdę dobrze sobie radzi z wyjaśnieniem różnicy między nimi. Aby go nie zaciemniać, oto tekst opisujący dynamiczne określanie zakresu:
źródło
Zasięg leksykalny oznacza, że funkcja wyszukuje zmienne w kontekście, w którym została zdefiniowana, a nie w zasięgu wokół niej.
Zobacz, jak działa zakres leksykalny w Lisp, jeśli chcesz uzyskać więcej szczegółów. Wybrana odpowiedź Kyle'a Cronina w zmiennych dynamicznych i leksykalnych w Common Lisp jest o wiele jaśniejsza niż odpowiedzi tutaj.
Przypadkowo dowiedziałem się o tym tylko w klasie Lisp, a zdarza się to również w JavaScript.
Uruchomiłem ten kod w konsoli Chrome.
Wynik:
źródło
Zakres leksykalny w JavaScript oznacza, że zmienna zdefiniowana poza funkcją może być dostępna wewnątrz innej funkcji zdefiniowanej po deklaracji zmiennej. Ale przeciwieństwo nie jest prawdą; zmienne zdefiniowane w funkcji nie będą dostępne poza tą funkcją.
Ta koncepcja jest szeroko stosowana w zamknięciach w JavaScript.
Powiedzmy, że mamy poniższy kod.
Teraz, kiedy wywołasz add () -> to wydrukuje 3.
Tak więc funkcja add () uzyskuje dostęp do zmiennej globalnej,
x
która jest zdefiniowana przed dodaniem funkcji metody. Jest to wywoływane ze względu na zakres leksykalny w JavaScript.źródło
add()
funkcja zostanie wywołana bezpośrednio po danym fragmencie kodu, wypisze również 3. Zasięg leksykalny nie oznacza po prostu, że funkcja może uzyskać dostęp do zmiennych globalnych poza kontekstem lokalnym. Tak więc przykładowy kod naprawdę nie pomaga pokazać, co oznacza zakres leksykalny. Pokazanie leksykalnego zakresu w kodzie naprawdę wymaga licznika lub przynajmniej wyjaśnienia innych możliwych interpretacji kodu.Zakres leksykalny odnosi się do leksykonu identyfikatorów (np. Zmiennych, funkcji itp.) Widocznych z bieżącej pozycji na stosie wykonania.
foo
ibar
zawsze znajdują się w leksykonie dostępnych identyfikatorów, ponieważ są globalne.Kiedy
function1
jest wykonywana, że ma dostęp do leksykonufoo2
,bar2
,foo
, ibar
.Kiedy
function2
jest wykonywana, że ma dostęp do leksykonufoo3
,bar3
,foo2
,bar2
,foo
, ibar
.Powodem, dla którego funkcje globalne i / lub zewnętrzne nie mają dostępu do identyfikatorów funkcji wewnętrznych, jest to, że wykonanie tej funkcji jeszcze nie nastąpiło i dlatego żaden z jej identyfikatorów nie został przydzielony do pamięci. Co więcej, po uruchomieniu wewnętrznego kontekstu jest on usuwany ze stosu wykonawczego, co oznacza, że wszystkie jego identyfikatory zostały wyrzucone do pamięci i nie są już dostępne.
Wreszcie, dlatego zagnieżdżony kontekst wykonania może ZAWSZE uzyskać dostęp do kontekstu wykonania przodków, a tym samym ma dostęp do większej leksykonu identyfikatorów.
Widzieć:
Specjalne podziękowania dla @ robr3rd za pomoc w uproszczeniu powyższej definicji.
źródło
Oto inny punkt widzenia na to pytanie, który możemy uzyskać, cofając się o krok i patrząc na rolę określania zakresu w szerszych ramach interpretacji (uruchamianie programu). Innymi słowy, wyobraź sobie, że budujesz interpreter (lub kompilator) dla języka i jesteś odpowiedzialny za obliczenie wyniku, biorąc pod uwagę program i pewne dane wejściowe.
Interpretacja obejmuje śledzenie trzech rzeczy:
Stan - mianowicie zmienne i odnośne lokalizacje pamięci na stercie i stosie.
Operacje w tym stanie - mianowicie każdy wiersz kodu w twoim programie
Środowisko , w którym dana operacja przebiega - mianowicie rzut stanu w operacji.
Tłumacz rozpoczyna się od pierwszego wiersza kodu w programie, oblicza jego środowisko, uruchamia wiersz w tym środowisku i rejestruje jego wpływ na stan programu. Następnie wykonuje kontrolę programu, aby wykonać następny wiersz kodu, i powtarza proces aż do zakończenia programu.
Sposób obliczania środowiska dla dowolnej operacji odbywa się za pomocą formalnego zestawu reguł określonych przez język programowania. Termin „wiązanie” jest często używany do opisania odwzorowania ogólnego stanu programu na wartość w środowisku. Zauważ, że przez „stan ogólny” nie rozumiemy stanu globalnego, ale sumę wszystkich możliwych do osiągnięcia definicji w dowolnym momencie wykonania).
Jest to struktura, w której zdefiniowano problem określania zakresu. Teraz do następnej części naszych opcji.
Jest to sedno dynamicznego określania zakresu , w którym środowisko, w którym działa dowolny kod, jest powiązane ze stanem programu określonym przez kontekst jego wykonania.
Innymi słowy, w zakresie leksykalnym środowisko, które widzi każdy kod, jest powiązane ze stanem powiązanym z zakresem zdefiniowanym jawnie w języku, takim jak blok lub funkcja.
źródło
Starożytne pytanie, ale oto moje zdanie.
Zakres leksykalny (statyczny) odnosi się do zakresu zmiennej w kodzie źródłowym .
W języku takim jak JavaScript, w którym funkcje mogą być przekazywane i dołączane oraz ponownie dołączane do różnych obiektów, możesz mieć taki zakres, który zależy od tego, kto wywołuje funkcję w danym momencie, ale tak nie jest. Zmiana zakresu w ten sposób byłaby zakresem dynamicznym, a JavaScript tego nie robi, chyba że z
this
odniesieniem do obiektu.Aby zilustrować tę kwestię:
W tym przykładzie zmienna
a
jest zdefiniowana globalnie, ale jest zaciemniona wdoit()
funkcji. Ta funkcja zwraca inną funkcję, która, jak widać, opiera się naa
zmiennej poza własnym zasięgiem.Jeśli to uruchomisz, przekonasz się, że użyta wartość
aardvark
nie jest tym ,apple
co, choć jest objęte zakresemtest()
funkcji, nie znajduje się w zakresie leksykalnym oryginalnej funkcji. Oznacza to, że zastosowany zakres jest zakresem, jaki pojawia się w kodzie źródłowym, a nie zakresem, w którym funkcja jest faktycznie używana.Ten fakt może mieć irytujące konsekwencje. Na przykład możesz zdecydować, że łatwiej jest zorganizować swoje funkcje osobno, a następnie użyć ich, gdy nadejdzie odpowiedni czas, na przykład w module obsługi zdarzeń:
Ten przykładowy kod wykonuje jedną z nich. Widać, że z powodu zakresu leksykalnego przycisk
A
używa zmiennej wewnętrznej, a przyciskB
nie. Możesz zagnieżdżać funkcje bardziej, niż byś chciał.Nawiasem mówiąc, w obu przykładach zauważysz również, że wewnętrzne zmienne o leksykalnym zasięgu pozostają, nawet jeśli funkcja zawierająca funkcję przeszła swój bieg. Nazywa się to zamknięciem i odnosi się do dostępu zagnieżdżonej funkcji do zmiennych zewnętrznych, nawet jeśli funkcja zewnętrzna zakończyła się. JavaScript musi być wystarczająco inteligentny, aby określić, czy te zmienne nie są już potrzebne, a jeśli nie, może je wyrzucić.
źródło
Zwykle uczę się na przykładach, a oto coś:
źródło
Temat ten jest ściśle powiązany z wbudowaną
bind
funkcją i wprowadzony w ECMAScript 6 Funkcje strzałek . To było naprawdę denerwujące, ponieważ dla każdej nowej metody „klasy” (właściwie funkcji), której chcieliśmy użyć, musieliśmybind
, aby mieć dostęp do zakresu.JavaScript domyślnie nie określa zakres jego
this
funkcji (to nie ustawia kontekst onthis
). Domyślnie musisz wyraźnie powiedzieć, który kontekst chcesz mieć.Funkcje strzałek automatycznie otrzymują tak zwany zakres leksykalny (mają dostęp do definicji zmiennej w jej bloku zawierającym). Podczas korzystania z funkcji strzałek automatycznie wiąże
this
się z miejscem, w którym funkcja strzałki została zdefiniowana w pierwszej kolejności, a kontekst tej funkcji strzałki jest zawarte w nim blok.Zobacz, jak to działa w praktyce na najprostszych przykładach poniżej.
Przed funkcjami strzałek (domyślnie brak zakresu leksykalnego):
Z funkcjami strzałek (domyślnie zakres leksykalny):
źródło