Zjawisko to jest znane jako: JavaScript Variable Hoisting .
W żadnym momencie nie uzyskujesz dostępu do zmiennej globalnej w swojej funkcji; zawsze uzyskujesz dostęp tylko do value
zmiennej lokalnej .
Twój kod jest równoważny z następującym:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Wciąż jesteś zaskoczony undefined
?
Wyjaśnienie:
Jest to coś, na co prędzej czy później wpada każdy programista JavaScript. Mówiąc najprościej, wszelkie zadeklarowane zmienne są zawsze przenoszone na szczyt lokalnego zamknięcia. Tak więc, nawet jeśli zadeklarowałeś swoją zmienną po pierwszym console.log
wywołaniu, nadal uważa się, że zadeklarowałeś ją wcześniej.
Jednak podnoszona jest tylko część deklaracji; z drugiej strony zadanie to nie jest.
Tak więc, kiedy pierwszy raz dzwoniłeś console.log(value)
, odnosiłeś się do swojej lokalnie zadeklarowanej zmiennej, która nie ma jeszcze nic przypisanego; stąd undefined
.
Oto kolejny przykład :
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
Jak myślisz, co to ostrzeże? Nie, nie tylko czytaj dalej, pomyśl o tym. Jaka jest wartość test
?
Jeśli powiedziałeś coś innego niż start
, byłeś w błędzie. Powyższy kod jest równoważny z tym:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
tak, aby nigdy nie wpływać na zmienną globalną.
Jak widać, bez względu na to, gdzie umieścisz swoją deklarację zmiennej, zawsze jest ona podnoszona na szczyt lokalnego zamknięcia.
Dygresja:
Dotyczy to również funkcji.
Rozważ ten fragment kodu :
test("Won't work!");
test = function(text) { alert(text); }
co da ci błąd odniesienia:
Uncaught ReferenceError: test nie jest zdefiniowany
To wyrzuca wielu programistów, ponieważ ten fragment kodu działa dobrze:
test("Works!");
function test(text) { alert(text); }
Jak już wspomniano, powodem tego jest to, że część przypisania nie jest podnoszona. Tak więc w pierwszym przykładzie, kiedy test("Won't work!")
została uruchomiona, test
zmienna została już zadeklarowana, ale nie ma jeszcze przypisanej do niej funkcji.
W drugim przykładzie nie używamy przypisywania zmiennych. Zamiast tego używamy prawidłowej składni deklaracji funkcji, która całkowicie podnosi funkcję.
Ben Cherry napisał doskonały artykuł na ten temat, odpowiednio zatytułowany JavaScript Scoping and Hoisting .
Przeczytaj to. Daje ci to pełny obraz ze szczegółami.
Byłem nieco rozczarowany, że problem został tutaj wyjaśniony, ale nikt nie zaproponował rozwiązania. Jeśli chcesz uzyskać dostęp do zmiennej globalnej w zakresie funkcji bez tworzenia przez funkcję najpierw niezdefiniowanej zmiennej lokalnej, odwołaj się do zmiennej jako
window.varName
źródło
Zmienne w JavaScript zawsze mają zasięg obejmujący całą funkcję. Nawet jeśli zostały zdefiniowane w środku funkcji, są widoczne wcześniej. Podobne zjawisko można zaobserwować przy podnoszeniu funkcji.
Biorąc to pod uwagę, pierwszy
console.log(value)
widzivalue
zmienną (wewnętrzną, która przesłania zewnętrznąvalue
), ale nie została jeszcze zainicjowana. Można o tym myśleć tak, jakby wszystkie deklaracje zmiennych zostały niejawnie przeniesione na początek funkcji (a nie najbardziej wewnętrzny blok kodu), podczas gdy definicje pozostają w tym samym miejscu.Zobacz też
źródło
Istnieje zmienna globalna
value
, ale gdy sterowanie wchodzi dotest
funkcji,value
deklarowana jest inna zmienna, która przesłania zmienną globalną. Ponieważ deklaracje zmiennych ( ale nie przypisania ) w JavaScript są podnoszone na początek zakresu, w którym są zadeklarowane://value == undefined (global) var value = 10; //value == 10 (global) function test() { //value == undefined (local) var value = 20; //value == 20 (local) } //value == 10 (global)
Zauważ, że to samo dotyczy deklaracji funkcji, co oznacza, że możesz wywołać funkcję, zanim pojawi się ona w kodzie:
test(); //Call the function before it appears in the source function test() { //Do stuff }
Warto również zauważyć, że gdy połączysz te dwa elementy w wyrażenie funkcyjne, zmienna będzie obowiązywać
undefined
do momentu przypisania, więc nie możesz wywołać funkcji, dopóki tak się nie stanie:var test = function() { //Do stuff }; test(); //Have to call the function after the assignment
źródło
Najprostszym sposobem zachowania dostępu do zmiennych zewnętrznych (nie tylko o zasięgu globalnym) jest oczywiście próba niezgłaszania ich ponownie pod tą samą nazwą w funkcjach; po prostu nie używaj tam var . Zaleca się stosowanie odpowiednich reguł nazewnictwa opisowego . Dzięki nim trudno będzie skończyć ze zmiennymi nazwanymi takimi jak wartość (ten aspekt niekoniecznie jest powiązany z przykładem w pytaniu, ponieważ nazwa zmiennej mogła zostać podana dla uproszczenia).
Jeśli funkcja może zostać ponownie wykorzystana w innym miejscu, a zatem nie ma gwarancji, że zmienna zewnętrzna faktycznie zdefiniowana w tym nowym kontekście, można użyć funkcji Eval . Działa wolno w tej operacji, więc nie jest zalecany do funkcji wymagających dużej wydajności:
if (typeof variable === "undefined") { eval("var variable = 'Some value';"); }
Jeśli zmienna o zasięgu zewnętrznym, do której chcesz uzyskać dostęp, jest zdefiniowana w nazwanej funkcji, może być najpierw dołączona do samej funkcji, a następnie dostępna z dowolnego miejsca w kodzie - czy to z głęboko zagnieżdżonych funkcji, czy programów obsługi zdarzeń poza wszystko inne. Zauważ, że dostęp do właściwości jest znacznie wolniejszy i wymagałby zmiany sposobu programowania, więc nie jest to zalecane, chyba że jest to naprawdę konieczne: Zmienne jako właściwości funkcji (JSFiddle) :
// (the wrapper-binder is only necessary for using variables-properties // via "this"instead of the function's name) var functionAsImplicitObjectBody = function() { function someNestedFunction() { var redefinableVariable = "redefinableVariable's value from someNestedFunction"; console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty); console.log('--> redefinableVariable: ', redefinableVariable); } var redefinableVariable = "redefinableVariable's value from someFunctionBody"; console.log('this.variableAsProperty: ', this.variableAsProperty); console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty); console.log('redefinableVariable: ', redefinableVariable); someNestedFunction(); }, functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody); functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime(); functionAsImplicitObject(); // (spread-like operator "..." provides passing of any number of arguments to // the target internal "func" function in as many steps as necessary) var functionAsExplicitObject = function(...arguments) { var functionAsExplicitObjectBody = { variableAsProperty: "variableAsProperty's value", func: function(argument1, argument2) { function someNestedFunction() { console.log('--> functionAsExplicitObjectBody.variableAsProperty: ', functionAsExplicitObjectBody.variableAsProperty); } console.log("argument1: ", argument1); console.log("argument2: ", argument2); console.log("this.variableAsProperty: ", this.variableAsProperty); someNestedFunction(); } }; return functionAsExplicitObjectBody.func(...arguments); }; functionAsExplicitObject("argument1's value", "argument2's value");
źródło
Miałem ten sam problem nawet przy zmiennych globalnych. Odkryłem, że mój problem polega na tym, że zmienna globalna nie utrzymuje się między plikami html.
<script> window.myVar = 'foo'; window.myVarTwo = 'bar'; </script> <object type="text/html" data="/myDataSource.html"></object>
Próbowałem odwołać się do myVar i myVarTwo w załadowanym pliku HTML, ale otrzymałem niezdefiniowany błąd. Krótko mówiąc, odkryłem, że mogę odnieść się do zmiennych za pomocą:
<!DOCTYPE html> <html lang="en"> <!! other stuff here !!> <script> var myHTMLVar = this.parent.myVar /* other stuff here */ </script> </html>
źródło