Function Expression vs Declaration: что выбрать в JavaScript?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему этот вопрос мучает даже опытных разработчиков?

Если вы хоть раз писали на JavaScript, то наверняка встречали оба этих способа объявления функций. Они выглядят похоже, но ведут себя совершенно по-разному. На Stack Overflow этот вопрос собрал тысячи просмотров и десятки страниц обсуждений. Почему? Потому что непонимание разницы между var functionName = function() {} и function functionName() {} приводит к багам, которые сложно отловить.

Сегодня мы раз и навсегда разберемся в этой теме. Вы узнаете, что такое function declaration и function expression, как работает hoisting, и когда какой способ использовать. Поехали!

Что скрывается за синтаксисом?

Начнем с базы. Первый вариант — это Function Declaration (объявление функции):

function greet() {
    console.log('Привет!');
}

Второй — Function Expression (функциональное выражение):

const greet = function() {
    console.log('Привет!');
};

Обратите внимание: в первом случае функция имеет имя и стоит в начале строки. Во втором — мы присваиваем анонимную функцию переменной greet. Но самое интересное начинается, когда мы смотрим, как JavaScript обрабатывает эти объявления.

Главное отличие: Hoisting (поднятие)

Hoisting — это механизм JavaScript, при котором объявления переменных и функций перемещаются вверх своей области видимости перед выполнением кода. Но работает он по-разному для declaration и expression.

Function Declaration поднимается целиком

Когда вы используете function functionName() {}, JavaScript «поднимает» всё объявление функции вверх. Это значит, что вы можете вызвать функцию до того, как она объявлена в коде:

sayHello(); // "Привет!"

function sayHello() {
    console.log('Привет!');
}

Код выполнится без ошибок. Это удобно, но может сбивать с толку новичков.

Function Expression не поднимается

С var functionName = function() {} история другая. Поднимается только объявление переменной, но не её значение. Функция остаётся на месте:

sayHello(); // TypeError: sayHello is not a function

var sayHello = function() {
    console.log('Привет!');
};

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

Кстати, если заменить var на let или const, ошибка будет другой — ReferenceError: Cannot access 'sayHello' before initialization. Это связано с тем, что let и const не инициализируются до момента объявления (так называемая «временная мёртвая зона»).

Область видимости: ещё один камень преткновения

И function declaration, и function expression подчиняются правилам области видимости. Но есть нюанс.

Function Declaration видна в пределах блока

Хотя function declaration поднимается, её область видимости ограничена блоком (в строгом режиме):

'use strict';
if (true) {
    function greet() {
        console.log('Внутри блока');
    }
    greet(); // "Внутри блока"
}
greet(); // ReferenceError: greet is not defined (в строгом режиме)

В нестрогом режиме такое объявление «вылезет» наружу, но полагаться на это не стоит.

Function Expression живёт в своей переменной

С function expression всё предсказуемо: функция доступна только там, где доступна переменная. Если переменная объявлена внутри блока, то и функция будет только там:

if (true) {
    const greet = function() {
        console.log('Внутри блока');
    };
    greet(); // "Внутри блока"
}
greet(); // ReferenceError: greet is not defined

Это делает function expression более безопасным и предсказуемым выбором для современного кода.

Именованные Function Expression: тёмная лошадка

Вы знали, что у function expression может быть имя? Да, это не обязательно анонимная функция:

const greet = function sayHello() {
    console.log('Привет!');
};

Такая конструкция называется Named Function Expression. Имя sayHello доступно только внутри самой функции (например, для рекурсии) и не загрязняет внешнюю область видимости:

const factorial = function calc(n) {
    if (n <= 1) return 1;
    return n * calc(n - 1); // используем имя для рекурсии
};

console.log(factorial(5)); // 120
console.log(calc); // ReferenceError: calc is not defined

Это удобно для отладки: в стеке вызовов будет отображаться имя функции, а не (anonymous).

Когда что использовать? Практические советы

Теперь, когда вы знаете разницу, давайте разберём, какой способ выбрать в реальном проекте.

Когда использовать Function Declaration

  • Глобальные функции-утилиты, которые нужны во всём скрипте. Например, formatDate() или validateEmail().
  • Когда важна читаемость и вы хотите, чтобы функция была доступна до её объявления (например, в рекурсивных алгоритмах, где функция вызывает саму себя).
  • Коллбэки в старом коде, где ещё используется var.

Когда использовать Function Expression

  • Коллбэки и обработчики событий: button.addEventListener('click', function() { ... }).
  • Современный код на ES6+: используйте const или let для объявления функций. Это делает код более предсказуемым.
  • Условное объявление функций: например, вы хотите определить разную реализацию в зависимости от условий:
let calculate;
if (typeof window !== 'undefined') {
    calculate = function() {
        // браузерная версия
    };
} else {
    calculate = function() {
        // серверная версия
    };
}
  • Методы объектов (хотя для этого есть синтаксический сахар — method() {}).

Сравнительная таблица: наглядное резюме

Чтобы закрепить материал, вот краткая таблица различий:

Характеристика Function Declaration Function Expression
Синтаксис function name() {} const name = function() {}

Блоги

Рекомендаций по книгам