Сразу вызываемые функции JavaScript

Введение Определение и вызов функций являются ключевыми практиками для освоения JavaScript и большинства других языков программирования. Обычно функция определяется до ее вызова в вашем коде. Выражения немедленно вызываемой функции (IIFE), произносимые как «iffy», являются распространенным шаблоном JavaScript, который выполняет функцию сразу после ее определения. Разработчики в первую очередь используют этот шаблон, чтобы гарантировать, что переменные доступны только в рамках определенной функции. В этой статье вы сначала узнаете

Вступление

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

Выражения немедленно вызываемой функции (IIFE), произносимые как «iffy», являются распространенным шаблоном JavaScript, который выполняет функцию сразу после ее определения. Разработчики в первую очередь используют этот шаблон, чтобы гарантировать, что переменные доступны только в рамках определенной функции.

В этой статье вы сначала узнаете о функциональных выражениях. После этого мы углубимся в IIFE - как их писать и когда их использовать. Наконец, мы обсудим, как let введенное в ECMAScript 6, обеспечивает более чистую альтернативу для некоторых сценариев использования IIFE.

Что такое функциональные выражения?

В JavaScript вы можете определить функцию двумя способами:

  1. Декларация
  2. Выражение

Объявления функций начинаются с function , за которым следует имя функции и любые аргументы, которые она может принимать. Например, мы можем создать функцию logName используя такое объявление:

 function logName(userName) { 
 console.log(`${userName}, you are awesome`); 
 }; 
 
 logName("Jane"); 

Из определения функции мы записываем любое заданное значение в message в консоль. Затем мы вызвали функцию с «Джейн, ты классная!», Которая выведет этот текст на консоль.

При определении функции с использованием объявлений функций функция поднимается . Поднятая функция или переменная помещается в верхнюю часть своей функциональной области, когда JavaScript выполняет код.

Примечание. Функциональная область относится к тому, где была определена функция. Например, если бы функция foo() содержала bar() , мы бы сказали, что функциональная область bar() - это foo() . Если foo() не был определен внутри функции, то foo() принадлежит глобальной области. Функции JavaScript в глобальной области видимости доступны для всего кода, который с ними работает.

На практике это поведение позволяет использовать функцию до ее определения. Например, предыдущий код можно переписать вот так, и он будет вести себя точно так же:

 logName(); 
 
 function logName(name) { 
 console.log(`${name}, you are awesome!`); 
 }; 

Функциональные выражения - это определения функций, которые назначаются переменной. Следовательно, наше logName() может стать выражением функции, если мы создадим его следующим образом:

 const logUserName = function logName(name) { 
 console.log(`${name}, you are awesome!`); 
 }; 
 
 logUserName("Jane"); 

В этом примере для вызова функции нам нужно использовать указанное имя переменной - logUserName . Это не меняет поведения функции, она по-прежнему выводит на консоль «You are awesome».

В отличие от объявлений функций, выражения функций не поднимаются. Эти функции доступны только тогда, когда интерпретатор JavaScript обрабатывает эту строку кода.

Например, если мы попытались вызвать logUserName() перед созданием его как выражения функции:

 logUserName("Jane"); 
 const logUserName = function logName(name) { 
 console.log(`${name}, you are awesome!`); 
 }; 

Получаем следующую ошибку:

 Uncaught ReferenceError: Cannot access 'logUserName' before initialization 

Еще одно различие между функциональными выражениями и объявлениями функций заключается в том, что функциональные выражения могут определять функции без имени.

Функции без имен называются анонимными функциями . Например, logUserName() также можно определить с помощью анонимной функции, подобной этой:

 const logUserName = function (name) { 
 console.log(`${name}, you are awesome!`); 
 }; 

Стрелочные функции

Стрелочные функции обеспечивают синтаксический сахар для функциональных выражений. Повторная реализация нашей logUserName с использованием стрелочной функции будет выглядеть так:

 const logUserName = (name) => { 
 console.log(`${name}, you are awesome!`); 
 } 

Прочтите « Стрелочные функции в JavaScript», чтобы узнать больше об этом синтаксисе и о том, как он влияет на область действия функции.

Теперь, когда мы знаем, как создавать различные функциональные выражения, давайте узнаем, как их сразу вызывать.

Что такое выражения немедленного вызова функций?

IIFE - это функции, которые выполняются сразу после определения.

Мы можем сделать любое функциональное выражение IIFE, заключив его в круглые скобки и добавив в конце следующую пару скобок:

 (function() { 
 // Code that runs in your function 
 })() 

В качестве альтернативы вы можете использовать синтаксис стрелки для создания IIFE следующим образом:

 (() => { 
 // Code that runs in your function 
 })() 

Скобки, окружающие определение функции, позволяют JavaScript знать, что он будет обрабатывать выражение функции. Последняя пара круглых скобок вызывает функцию.

Варианты синтаксиса

Вы можете создавать IIFE без первого набора круглых скобок, если используете унарный оператор - специальные символы, которые сообщают JavaScript, что нужно вычислить следующее выражение.

Мы можем создавать функциональные выражения с унарными операторами, например:

 +function () { 
 // Code that runs in your function 
 }(); 
 
 -function () { 
 // Code that runs in your function 
 }(); 
 
 !function () { 
 // Code that runs in your function 
 }(); 
 
 ~function () { 
 // Code that runs in your function 
 }(); 
 
 void function () { 
 // Code that runs in your function 
 }(); 

Важно отметить, что эти операторы могут повлиять на любые данные, возвращаемые вашей функцией. Например, приведенный ниже код выглядит так, как будто он должен вернуть 10 , но на самом деле он возвращает -10 :

 $ node 
 > -function () {return 10;}(); 
 -10 
 > 

Поскольку этот унарный синтаксис менее распространен и может сбивать с толку разработчиков, его обычно не рекомендуется использовать.

IIFE также могут принимать функциональные аргументы. Мы можем передавать переменные в область видимости, как показано ниже:

 (function(arg1, arg2) { 
 // Code that runs in your function 
 })("hello", "world"); 

Теперь, когда мы увидели, как создавать IIFE, давайте рассмотрим типичные ситуации, в которых они используются.

Когда использовать IIFE?

Наиболее распространенные варианты использования IIFE:

  • Псевдонимы глобальных переменных
  • Создание частных переменных и функций
  • Асинхронные функции в циклах

Сопоставление глобальных переменных

Если у вас есть 2 библиотеки, которые экспортируют объект с тем же именем, вы можете использовать IIFE, чтобы гарантировать, что они не конфликтуют в вашем коде. Например, библиотеки jQuery и Cash JavaScript экспортируют $ качестве основного объекта.

Вы можете заключить свой код в IIFE, который передает одну из глобальных переменных в качестве аргумента. Допустим, мы хотим убедиться, что $ относится к jQuery , а не к cash альтернативе. Вы можете убедиться, что jQuery используется со следующим IIFE:

 (function($) { 
 // Code that runs in your function 
 })(jQuery); 

Создание частных переменных и функций

Мы можем использовать IIFE для создания частных переменных и функций в глобальной области видимости или любой другой области видимости функции.

Функции и переменные, добавленные в глобальную область видимости, доступны для всех скриптов, загружаемых на страницу. Допустим, у нас есть функция generateMagicNumber() , которая возвращает случайное число от 900 до 1000 включительно, а также переменную favoriteNumber в нашем файле JavaScript.

Мы можем написать их так:

 function generateMagicNumber() { 
 return Math.floor(Math.random() * 100) + 900; 
 } 
 
 console.log("This is your magic number: " + generateMagicNumber()); 
 
 var favoriteNumber = 5; 
 console.log("Twice your favorite number is " + favoriteNumber * 2); 

Если мы загружаем другие файлы JavaScript в наш браузер, они также получают доступ к функциям generateMagicNumber() и favoriteNumber . Чтобы предотвратить их использование или редактирование, мы заключаем наш код в IIFE:

 (function () { 
 function generateMagicNumber() { 
 return Math.floor(Math.random() * 100) + 900; 
 } 
 
 console.log("This is your magic number: " + generateMagicNumber()); 
 
 var favoriteNumber = 5; 
 console.log("Twice your favorite number is " + favoriteNumber * 2); 
 })(); 

Он работает так же, но теперь generateMagicNumber() и favoriteNumber доступны только в нашем скрипте.

Асинхронные функции в циклах

Поведение JavaScript многих удивляет, когда обратные вызовы выполняются в циклах. Например, давайте посчитаем от 1 до 5 в JavaScript, добавляя интервал в 1 секунду между каждой записью сообщения. Наивная реализация будет:

 for (var i = 1; i <= 5; i++) { 
 setTimeout(function () { 
 console.log('I reached step ' + i); 
 }, 1000 * i); 
 } 

Если вы запустите этот код, вы получите следующий результат:

 $ node naiveCallbackInLoop.js 
 I reached step 6 
 I reached step 6 
 I reached step 6 
 I reached step 6 
 I reached step 6 

Хотя выходные данные будут напечатаны через 1 секунду, каждая строка напечатает, что они достигли шага 6. Почему?

Когда JavaScript встречает асинхронный код, он откладывает выполнение обратного вызова до завершения асинхронной задачи. Вот так и остается неблокирующей. В этом примере console.log() будет запущен только по истечении тайм-аута.

JavaScript также создал закрытие для нашего обратного вызова. Замыкания - это комбинация функции и ее области при создании . С замыканиями наш обратный вызов может получить доступ к переменной i даже если for уже завершился.

Однако наш обратный вызов имеет доступ только к значению i во время его выполнения . Поскольку весь код в функции setTimeout() был отложен, for завершился с i , равным 6. Вот почему все они регистрируют, что достигли шага 6.

Эту проблему можно решить с помощью IIFE:

 for (var i = 1; i <= 5; i++) { 
 (function (step) { 
 setTimeout(function() { 
 console.log('I reached step ' + step); 
 }, 1000 * i); 
 })(i); 
 } 

Используя IIFE, мы создаем новую область видимости для нашей функции обратного вызова. Наш IIFE делает step в параметрах. Каждый раз, когда вызывается наш IIFE, мы передаем ему текущее значение i качестве аргумента. Теперь, когда обратный вызов готов к выполнению, его закрытие будет иметь правильное значение step .

Если мы запустим этот фрагмент кода, мы увидим следующий результат:

 $ node iifeCallbackInLoop.js 
 I reached step 1 
 I reached step 2 
 I reached step 3 
 I reached step 4 
 I reached step 5 

Хотя IIFE решает нашу проблему с минимальными изменениями кода, давайте посмотрим, как функции ES6 могут упростить выполнение асинхронного кода в циклах.

Блокировать область видимости с помощью let и const

ES6 добавил let и const для создания переменных в JavaScript. Переменные, объявленные с помощью let или const имеют блочную область видимости . Это означает, что к ним можно получить доступ только в пределах их ограничивающего блока - области, заключенной в фигурные скобки { } .

Давайте посчитаем от 1 до 5 с интервалом в 1 секунду, используя ключевое слово let var :

 for (let i = 1; i <= 5; i++) { 
 setTimeout(function () { 
 console.log('I reached step ' + i); 
 }, 1000 * i); 
 } 

Когда мы запустим этот код, мы получим следующий результат:

 $ node es6CallbackInLoop.js 
 I reached step 1 
 I reached step 2 
 I reached step 3 
 I reached step 4 
 I reached step 5 

Теперь, когда переменная i имеет блочную область видимости, замыкания для нашей функции обратного вызова получают соответствующее значение i когда они в конечном итоге выполняются. Это более кратко, чем наша реализация IIFE.

Использование let - предпочтительный способ выполнения асинхронных функций в цикле,

Заключение

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

Несмотря на популярность, мы видели, как изменения в ES6 могут устранить необходимость использования IIFE в современном JavaScript. Однако овладение этим шаблоном также дает нам более глубокое понимание области видимости и замыкания и будет особенно полезно при поддержке устаревшего кода JavaScript.

comments powered by Disqus