Dlaczego powiązania let i var zachowują się inaczej używając funkcji setTimeout? [duplikat]
To pytanie ma już odpowiedź tutaj:
- Jaka jest różnica między używaniem" let "i" var " do deklarowania zmiennej w JavaScript? 27 odpowiedzi
Ten kod loguje 6
, 6 razy:
(function timer() {
for (var i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
Ale ten kod...
(function timer() {
for (let i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
... zapisuje następujący wynik:
0
1
2
3
4
5
Dlaczego?
Czy dlatego, że let
wiąże się z wewnętrznym scope każdy element inaczej i var
zachowuje ostatnią wartość i
?
2 answers
Z var
masz zakres funkcji i tylko jedno wspólne Wiązanie dla wszystkich iteracji pętli - tzn. i
w każdym wywołaniu zwrotnym setTimeout oznacza tę samą zmienną, która W końcu jest równa 6 po zakończeniu iteracji pętli.
Z let
masz zakres blokowy i gdy użyjesz pętli for
, otrzymasz nowe Wiązanie dla każdej iteracji - tzn. i
w każdym wywołaniu zwrotnym setTimeout oznacza inną zmienną, każda z która ma inną wartość: pierwsza to 0, następna to 1 itd.
Więc to:
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}
})();
Jest równoważne z tym używając tylko var:
(function timer() {
for (var j = 0; j <= 5; j++) {
(function () {
var i = j;
setTimeout(function clog() { console.log(i); }, i * 1000);
}());
}
})();
Użycie natychmiast wywołanego wyrażenia funkcji do użycia zakresu funkcji w podobny sposób, jak zakres bloku działa w przykładzie z let
.
Mogłoby być napisane krócej bez użycia j
nazwy, ale być może nie byłoby tak jasne:
(function timer() {
for (var i = 0; i <= 5; i++) {
(function (i) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}(i));
}
})();
I jeszcze krótszy ze strzałką funkcje:
(() => {
for (var i = 0; i <= 5; i++) {
(i => setTimeout(() => console.log(i), i * 1000))(i);
}
})();
(ale jeśli możesz używać funkcji strzałek, nie ma powodu, aby używać var
.)
Oto jak Babel.js tłumaczy twój przykład za pomocą let
, aby działał w środowiskach, w których let
nie jest dostępny:
"use strict";
(function timer() {
var _loop = function (i) {
setTimeout(function clog() {
console.log(i);
}, i * 1000);
};
for (var i = 0; i <= 5; i++) {
_loop(i);
}
})();
Podziękowania dla Michaela Geary za umieszczenie linku do Babel.js w komentarzach. Zobacz link w komentarzu na żywo demo, gdzie można zmienić cokolwiek w kodzie i oglądać tłumaczenie odbywa się natychmiast. Ciekawe jak inne funkcje ES6 również tłumaczone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-08 08:42:08
Technicznie tak wyjaśnia @rsp w swojej doskonałej odpowiedzi. Tak lubię rozumieć, że wszystko działa pod maską. Dla pierwszego bloku kodu używając var
(function timer() {
for (var i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
Możesz sobie wyobrazić, że kompilator działa w ten sposób wewnątrz pętli for
setTimeout(function clog() {console.log(i)}, i*1000); // first iteration, remember to call clog with value i after 1 sec
setTimeout(function clog() {console.log(i)}, i*1000); // second iteration, remember to call clog with value i after 2 sec
setTimeout(function clog() {console.log(i)}, i*1000); // third iteration, remember to call clog with value i after 3 sec
I tak dalej
Ponieważ i
jest zadeklarowane za pomocą var
, gdy wywołane jest clog
, kompilator znajduje zmienną i
w najbliższym bloku funkcji, który jest timer
i ponieważ osiągnęliśmy już koniec pętli for
, i
przechowuje wartość 6 i wykonuje clog
. To tłumaczy, że 6 było zalogowanych 6 razy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-08 07:56:42