Вызов самовыполняющейся функции из прослушивателя событий

Предположим, это мой единственный HTML

<input id="target" type="number">

И предположим, что это мой единственный JavaScript

var target = document.getElementById('target');

Я хочу выполнять function при каждом изменении ввода, но я также хочу выполнить указанное function один раз при загрузке страницы (Self-Executing Function или IIFE). Ниже приведены 3 примера, 1 из которых не работает.



Следующее работает, как и ожидалось:

target.addEventListener('input', myFunction);

function myFunction(){
    console.log('executed!');
}
myFunction();

Здесь function будет выполняться при загрузке страницы. Он не будет выполняться eventListener, вместо этого он выведет ReferenceError: myFunction is not defined на консоль:

target.addEventListener('input', function(){
    myFunction();
});

(function myFunction(){
    console.log('executed!');
})();

Этот не будет выполняться ни при загрузке страницы, ни по eventListener и выведет ReferenceError: myFunction is not defined в консоль:

target.addEventListener('input', myFunction);

(function myFunction(){
    console.log('executed!');
})();


Мой вопрос: почему третий пример не работает?


person Jeweetzelf    schedule 18.04.2018    source источник
comment
Я не думаю, что второй пример кода будет работать, если вы также не объявили myFunction где-то еще. У него будет точно такая же проблема, как и при третьей попытке.   -  person Pointy    schedule 18.04.2018
comment
Вы правы, я изменил его   -  person Jeweetzelf    schedule 18.04.2018
comment
на самом деле во втором случае вы объявили функцию в глобальной области, но в третьем случае вы просто создали функцию в области блока и выполнили ее.   -  person Ravi Sevta    schedule 18.04.2018


Ответы (1)


Весь смысл IIFE состоит в том, чтобы предотвратить загрязнение глобального (или более высокого) масштаба. Это не работает, потому что немедленно вызываемое функциональное выражение (IIFE) является анонимной функцией. Итак, когда вы настраиваете свой синтаксис IIFE с именем, имя функции игнорируется вне выражения.

Из MDN:

IIFE (Immediately Invoked Function Expression) — это функция JavaScript, которая запускается, как только она определена.

Это шаблон проектирования, который также известен как самовыполняющаяся анонимная функция и состоит из двух основных частей. Первая — это анонимная функция с лексической областью действия, заключенная в операторе группировки (). Это предотвращает доступ к переменным в рамках идиомы IIFE, а также загрязняет глобальную область видимости.

Вторая часть — это создание немедленно исполняемого функционального выражения (), с помощью которого движок JavaScript будет напрямую интерпретировать функцию.

Кроме того, ваш второй пример на самом деле не работает по той же причине. Вы видите свое сообщение executed!, но это потому, что IIFE выполняется автоматически. Если вы продолжите изменять значение ввода, вы получите ту же ошибку, что и при варианте №3.

var target = document.getElementById('target');

target.addEventListener('input', function(){
    myFunction();
});

(function myFunction(){
    console.log('executed!');
})();
<input id="target" type="number">

person Scott Marcus    schedule 18.04.2018
comment
Не могу представить, почему за это проголосовали. Ответ правильный, у него есть подтверждающая документация и работающий пример. - person Scott Marcus; 18.04.2018
comment
Это не работает, потому что выражение немедленно вызываемой функции (IIFE) является анонимной функцией, что неверно. IIFE является анонимным, если вы не называете его. Но в примере OP он назван. Проблема заключается в области охвата. Удалите свой ответ. - person marekful; 18.04.2018
comment
@marekful Нет, это не так. Хотя OP назвал функцию, это имя игнорируется, поскольку предполагается, что IIFE является анонимной функцией. Прочитайте мою документацию из MDN, в которой объясняется, что синтаксис внутри оператора группировки настроен так, чтобы не загрязнять глобальную область. В этом весь смысл. - person Scott Marcus; 18.04.2018
comment
@marekful Это та же идея, что и var x = function foo(){...}. В функциональном выражении имя игнорируется. Вы не можете выполнить foo() в этом примере, только x(); - person Scott Marcus; 18.04.2018
comment
Спасибо! У меня сложилось впечатление, что название IIFE действительно имело смысл. Я прочитал статью об этом (benalman.com/news/2010/11/), который показывает несколько примеров именованного IIFE, даже автор утверждает, что немедленно вызываемое функциональное выражение может быть либо анонимным, либо именованным, но он не упоминает, что присвоение ему имени на самом деле ничего не делает. - person Jeweetzelf; 18.04.2018
comment
Вы можете «экспортировать» ссылку на IIFE, только если она именована. Рассмотрим это var n; (function someName() { n = someName; })(). Я по-прежнему утверждаю, что эти ссылки недоступны, потому что круглые скобки, используемые для переноса выражения, создают область, которая недоступна для внешней области. - person marekful; 18.04.2018
comment
@marekful нет, имя именованной функции expression никогда не экспортируется в охватывающую область. (И в любом случае круглые скобки никогда не влияют на область действия.) Имя видно только внутри функции. - person Pointy; 18.04.2018
comment
@marekful Скобки не имеют ничего общего с областью действия, они просто заключают выражение, которое превратит то, что было бы объявлением функции, в выражение функции. Функциональные выражения не поднимаются независимо от того, сколько круглых скобок вы окружаете их, поэтому, даже если вы можете назвать их, это имя не будет видно за пределами их тела. Именование этих функциональных выражений имеет мало смысла, за исключением тривиальных примеров, таких как определение факториально-рекурсивного IIFE, поэтому сами ребята из MDN называют их «анонимными». Хотя я нахожу формулировку довольно плохой, она явно указывает на то, что они не подняты. - person kuroi neko; 21.04.2018
comment
@kuroineko Хотя это правда, что функциональные выражения не поднимаются, это не причина того, что имя в именованном функциональном выражении недоступно вне выражения. Если бы речь шла только о подъеме, то вы могли бы получить доступ к имени функции в коде, который появляется после объявления выражения функции, чего, как мы знаем, у вас нет. - person Scott Marcus; 21.04.2018
comment
ну ладно, их имя вообще не экспортируется в текущую область. Вот к чему относится анонимный термин. - person kuroi neko; 24.04.2018