Почему IIFE необходим для создания новой области?
От вас не знают, JS:
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
дает
6
6
6
6
6
но с использованием IIFE, как так
for (var i=1; i<=5; i++) {
(function(){
var j = i;
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})();
}
дает
1
2
3
4
5
Мой вопрос: почему не
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
var j = i;
console.log( j );
}, i*1000 );
}
или же
for (var i=1; i<=5; i++) {
function timer() {
var j = i;
console.log(j);
}
setTimeout(timer, i*1000 );
}
работать как пример IIFE? Мне кажется, у них обоих есть function
объявление с новой переменной j
разве это не создаст новую лексическую область с определенным параметром для i
?
3 ответа
Важной частью IIFE является то, что он работает сразу же; до i
изменяется, он читает его значение и помещает его в новую переменную. Функция чтения i
в других ваших примерах - function timer()
- не запускается сразу, и значение, которое он помещает в новую переменную, является значением i
после того, как это уже изменилось.
Также в ES6 вы можете просто let i = …
вместо var i = …
и это будет нормально работать без IIFE или j
:
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
так как let
имеет область видимости блока вместо области видимости функции и переменных, объявленных в части инициализации for
петли считаются половиной внутри for
Блок.
i
будучи объявленным с var
, лапы. Переменные не привязывают свои области автоматически к внутренней функции; если внутренняя функция явно не имеет var i
или параметр i
(таким образом, определяя новый i
привязаны к сфере действия внутренней функции), i
будет продолжать ссылаться на лапы i
во внешнем объеме.
Например, вы можете сделать то, о чем думали, если хотите:
for (var i=1; i<=5; i++) {
setTimeout( function timer(i){
console.log( i );
}, i*1000, i );
}
(Третий аргумент setTimeout
это то, с чем функция, второй аргумент, будет вызываться)
Это означает, что timer
будет вызван с i
как во время итерации, и функция будет использовать новый i
, привязанный к области действия функции, инициализируется через параметр.
Это довольно плохая идея - лучше использовать const
а также let
, которые имеют область видимости блока, а не область функции, и лучше не скрывать внешние переменные.
Этот вид IIFE
for (var i=1; i<=5; i++) {
(function(){
var j = i;
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})();
}
часто пишется как
for (var i=1; i<=5; i++) {
(function(j){
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})(i);
}
Итак, вы можете увидеть, что "захваченное" значение i
в этом случае
Вы можете сделать то же самое без IIFE
for (var i=1; i<=5; i++) {
function timer(j) {
setTimeout(function() {
console.log(j);
}, j * 1000 );
}
timer(i);
}
конечно, это эквивалентно
function timer(j) {
setTimeout(function() {
console.log(j);
}, j * 1000 );
}
for (var i=1; i<=5; i++) {
timer(i);
}
если вы используете ES2015+, вы можете использовать let
for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
Теперь, если вы используете транспортер, потому что вам нужно поддерживать ES5 (или любой другой браузер Internet Explorer), вы увидите, что транспортируемая версия
var _loop = function _loop(i) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
};
for (var i = 1; i <= 5; i++) {
_loop(i);
}
Который невероятно похож на предыдущую версию кода