Тип Поста

JavaScript – разница между объявлением функции и её выражением

JavaScript – разница между объявлением функции и её выражением

В JavasScript существует два различных способа создания функции. Первый способ – это обычное объявление, которое широко используется разработчиками уже долгое время. И второй способ – создание функции в качестве выражения, которое присваивается переменной. Именно этот способ постепенно приобретает популярность.

На примере хорошо видно разницу между этими двумя способами:

function myFuncDeclaration() 
{
  return 'Объявление функции';
}

var myFuncExpression = function () 
{
  return 'Выражение функции';
}

Разница между объявлением и выражением функции

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

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

Выгода использования функциональных выражений

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

  • В качестве замыканий
  • Как аргумент для другой функции
  • В случае немедленно вызываемой функции (IIFE)

Создание замыканий

Замыкания используются, когда вы хотите передать параметр в функцию до того, как она будет выполнена. Хорошим примером для замыкания послужит случай, когда необходимо пройтись в цикле по элементам DOM-дерева. Замыкание позволит сохранить в себе необходимую информацию, такую, как номер индекса итерации цикла, когда функция уже выполнена.

var tabs = document.querySelectorAll( '.tab' );

for ( var i = 0; i < tabs.length; i++ ) 
{
  /* Пытаемся в цикле повесить обработчик на каждый элемент */
  tabs[ index ].onclick = tabsHandler( i );
}

/* Функция обработчик */
function tabsHandler( index ) 
{
  return function tabClickEvent( evt )
  {
    /*
    *  Далее идет код для нужной обработки
    *  Переменная index здесь недоступна
    */
  };
}

Функция обработчик выполнится после того, как цикл завершится, в этом случае уместно будет использовать замыканием, которое позволит сохранить соответствующее значение index.

var list = document.querySelectorAll( '.item' );

/* Пример, почему замыкание необходимо */
var doSomething = function ( evt ) 
{
  /* К моменту, когда эта функция будет исполняться, значение i всегда равно list.length */
};

for ( var i = 0; i < list.length; i++ ) 
{
    item[ i ].onclick = doSomething;
}

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

Следующий пример показывает применение замыкания:

var list = [ 'item1', 'item2', 'item3' ];

for ( var i = 0; i < list.length; i++ )
{
  /* Вешаем событие на каждый элемент */
  list[ I ].onclick = doSomethingHandler( i );
}

/* Внешняя функция, которая будет хранить верное значение индекса массива */
doSomethingHandler = function ( itemIndex ) 
{
  /* Внутренняя функция, которая благодаря замыканию, будет иметь доступ к верному значению */
  return function doSomething( evt ) 
  {
   /* Также здесь могут быть доступны любые другие нужные переменные */
   console.log( 'Что-то сделали с ' + list[ itemIndex ] );
  };
};

Передача функции в качестве аргумента

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

Зачастую, это можно увидеть в jQuery, в качестве анонимной функции:

$( document ).ready( function ()
{
    console.log( 'Анонимная функция' );
});

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

var productIds = [ '11', '12', '13' ];

productIds.forEach( function showProduct( productId )
{
  /* Какой-то код */
});

Немедленно исполняемые функциональные выражения (IIFE)

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

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

Самый простой пример немедленно исполняемой функции выглядит таким образом:

( function ()
{
  /* Здесь идет код */
}() );

И пример модульного подхода, благодаря которому можно достичь хорошей поддерживаемости написанного кода:

var myModule = ( function ()
{
  var privateMethod = function ()
  {
    console.log( 'Это приватный метод' );
  },
  someMethod = function ()
  {
    console.log( 'Это публичный метод' );
  },
  anotherMethod = function ()
  {
    console.log( 'Еще один публичный метод' );
  };
  
  /* Даем доступ к необходимым методам */
  return {
    someMethod: someMethod,
    anotherMethod: anotherMethod
  };
}() );

И напоследок…

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

При создании статьи были использованы следующие источники:

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
  2. http://www.sitepoint.com/function-expressions-vs-declarations/
Поделиться

Оставить комментарий

Вы можете использовать следующие HTML-теги:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Обязательно к заполнению