Встречается на 20% собеседований по JavaScript

Что знаешь о различии между function expression и function declaration

Function Declaration (объявление функции) и Function Expression (функциональное выражение) — это два основных способа определения функций в JavaScript. Главное различие между ними заключается в механизме поднятия (hoisting): объявления функций поднимаются вверх своей области видимости и доступны до выполнения кода, тогда как функциональные выражения доступны только после того, как интерпретатор дойдёт до их строки. Это влияет на то, когда и как можно использовать функции в коде.

Как это работает

Function Declaration

Function Declaration — это способ объявления функции с использованием ключевого слова function, за которым следует имя функции. Пример:

javascript
function sum(a, b) {
  return a + b;
}

Особенности:

  • Поднятие (hoisting): объявление функции полностью поднимается вверх своей области видимости. Это означает, что функцию можно вызвать до её объявления в коде. Например, следующий код работает без ошибок:
javascript
console.log(sum(2, 3)); // 5
function sum(a, b) {
  return a + b;
}
  • Область видимости: функция доступна в пределах блока, в котором она объявлена. В строгом режиме ('use strict') блочная область видимости ограничивает видимость функции внутри блока {}.

  • Имя функции: имя является неизменным и не может быть переназначено. Попытка присвоить другое значение имени функции приведёт к ошибке в строгом режиме или будет проигнорирована в нестрогом.

Function Expression

Function Expression — это способ создания функции в рамках выражения. Функция присваивается переменной или передаётся как аргумент. Пример:

javascript
const sum = function(a, b) {
  return a + b;
};

Особенности:

  • Поднятие: переменная, которой присвоена функция, поднимается, но её значение не инициализируется. Если используется var, переменная будет иметь значение undefined до строки присваивания, поэтому вызов функции до этого места приведёт к ошибке TypeError. Если используется let или const, переменная попадает во временную мёртвую зону (temporal dead zone), и доступ к ней до объявления также вызовет ошибку.
javascript
console.log(sum(2, 3)); // TypeError: sum is not a function
var sum = function(a, b) {
  return a + b;
};
  • Анонимные и именованные: функциональные выражения могут быть анонимными (без имени) или именованными. Именованные выражения полезны для отладки и рекурсии, так как имя доступно внутри самой функции.
javascript
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};
  • Использование в выражениях: функциональные выражения можно передавать как аргументы в другие функции, например, в setTimeout или map, что делает их удобными для колбэков.

Примеры использования

Рассмотрим практический пример, демонстрирующий разницу в поднятии:

javascript
// Function Declaration
console.log(declared()); // "declared"
function declared() {
  return "declared";
}

// Function Expression
console.log(expression()); // ReferenceError: Cannot access 'expression' before initialization
const expression = function() {
  return "expression";
};

В первом случае функция declared вызывается до объявления, и это работает. Во втором случае expression объявлена через const, поэтому до строки инициализации она находится во временной мёртвой зоне, и обращение к ней вызывает ошибку.

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

  • Function Declaration предпочтительнее, когда функция нужна в нескольких местах кода и её вызов может происходить до объявления. Это делает код более читаемым, так как функции можно размещать внизу модуля.

  • Function Expression используется, когда функция нужна как значение: для присваивания переменной, передачи в качестве аргумента или создания замыкания. Это даёт большую гибкость, например, можно переопределить функцию в зависимости от условий.

Подводные камни

  • Блочная область видимости: в строгом режиме Function Declaration внутри блока {} видна только внутри этого блока. Это может привести к неожиданным ошибкам, если предполагать, что функция доступна снаружи.

  • Временная мёртвая зона: при использовании let или const для функционального выражения, попытка вызвать функцию до объявления приведёт к ReferenceError, а не к TypeError, как с var.

  • Именованные функциональные выражения: имя внутри функции доступно, но снаружи оно не видно. Это важно для рекурсии, но не для внешнего вызова.

Коротко

  • Function Declaration поднимается полностью, Function Expression — нет.
  • Function Declaration имеет неизменное имя, Function Expression может быть анонимным или именованным.
  • Function Expression удобно использовать для колбэков и присваивания переменным.
  • В строгом режиме Function Declaration ограничен блоком, а let/const создают временную мёртвую зону для выражений.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы