почему IIFE необходим для создания новой области? - PullRequest
0 голосов
/ 31 мая 2018

С Вы не знаете 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 ]

0 голосов
/ 31 мая 2018

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 , как во время итерации , и функция будет использовать new i, привязанный к области действия функции, инициализированной через параметр.

Это довольно плохая идея - лучше использовать const и let, которые имеют область видимости блока, а не область действия функции, и лучше не скрывать внешние переменные.

0 голосов
/ 31 мая 2018

Этот сорт 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);
}

, что невероятно похоже на предыдущую версиюкод

0 голосов
/ 31 мая 2018

Важной частью 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.блок.

...