Способы объявления функций в JavaScript

Какие способы объявления функции есть в JavaScript?

Теория: что считается “объявлением”

В JavaScript функция создаётся либо как декларация (statement), либо как выражение (expression), которое вычисляется и даёт значение-функцию.
Декларация обычно создаёт именованную привязку сразу в области видимости, а выражение создаёт значение, которое затем присваивается переменной или свойству объекта.
Отдельно выделяется синтаксис методов в объектах и классах: внешне это похоже на функцию, но в поведении есть отличия (например, методы в краткой записи не предназначены для вызова через new).

Объявления функций внутри блоков в нестрогом режиме (if, for, { ... } без "use strict") могут вести себя по-разному в разных окружениях, поэтому такой стиль считается нежелательным.

Основные способы и различия

Function Declaration: function name() { ... }

Декларация создаёт функцию и имя для неё.
Ключевое свойство декларации — поднятие (hoisting): вызов функции допустим до строки, где она записана, если вызов находится в той же области видимости.

Пример:

callBefore();
function callBefore() {
  console.log("вызов возможен до объявления");
}

Function Expression: const f = function() { ... }

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

Пример:

console.log(typeof notReady); // "undefined" (для var) или ReferenceError (для let/const)
var notReady = function () {
  console.log("готово");
};
notReady(); // корректно только после присваивания
Именованное функциональное выражение удобно для рекурсии, потому что имя видно только внутри тела функции и не добавляется в внешнюю область видимости.

Пример:

const math = {
  factorial: function factorial(n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
  }
};
console.log(math.factorial(5));

Arrow Function Expression: (...) => ...

Стрелочная функция является выражением и часто применяется для колбэков из-за краткого синтаксиса.
У стрелочной функции нет собственного this и arguments, поэтому поведение this берётся из внешнего окружения, что важно при использовании внутри методов объектов.
Стрелочную функцию нельзя вызывать с new, и в ней нельзя использовать yield, поэтому генераторы через => не объявляются.

Пример:

const sum = (a, b) => a + b;
const obj = {
  value: 10,
  bad: () => console.log(this.value),
  good() { console.log(this.value); }
};
obj.bad();
obj.good();

Method Definition: методы в объекте и классе

Краткая запись метода является специальным синтаксисом для объявления функции как свойства объекта или как метода класса.
Такой метод не является конструктором: попытка создать объект через new от метода приводит к ошибке типа.
В классе методы обычно находятся в прототипе, поэтому один и тот же код метода разделяется всеми экземплярами.

Пример:

const obj2 = {
  method() { return 1; }
};
class C {
  method() { return 2; }
}
console.log(obj2.method());
console.log(new C().method());

Generator Function Declaration: function*

Генераторная декларация function* создаёт генератор, который умеет “останавливать” выполнение на yield и продолжать позже, сохраняя внутреннее состояние.
Вызов генератора не выполняет тело сразу, а возвращает итератор; выполнение продвигается вызовами next(), которые возвращают объект вида { value, done }.

Пример:

function* counter(start) {
  let i = start;
  while (true) {
    yield i;
    i++;
  }
}
const it = counter(10);
console.log(it.next());
console.log(it.next());
Конструкция yield* применяется для делегирования итерации другому генератору.

Пример:

function* genA() {
  yield 1;
  yield 2;
}
function* genB() {
  yield 0;
  yield* genA();
  yield 3;
}
console.log([...genB()]);

Async Function Declaration: async function

async function создаёт асинхронную функцию, внутри которой разрешён await, а вызов возвращает Promise.
Между async и function не следует размещать перевод строки, иначе в некоторых ситуациях возможна автоматическая вставка точки с запятой и изменение смысла.

Пример:

async function loadValue() {
  const value = await Promise.resolve(123);
  return value;
}
loadValue().then(console.log);

Function constructor: Function(...) / new Function(...)

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

Динамическая генерация кода строками (Function, eval) потенциально опасна при работе с недоверенным вводом, так как может привести к выполнению чужого кода.

Пример:

const add = new Function("a", "b", "return a + b");
console.log(add(2, 3));
function outer() {
  const secret = 42;
  const f = new Function("return typeof secret");
  return f();
}
console.log(outer());

Схема и таблица

Ниже приведена упрощённая схема выбора формы объявления по задаче (чтение сверху вниз).

Нужно именованную функцию, доступную до места записи в области видимости?
-> Function Declaration

Нужно передать функцию как значение (колбэк, аргумент, результат)?
-> Function Expression или Arrow Function

Нужно корректное `this` как у метода объекта или класса?
-> Method Definition (или `function` в свойстве), но обычно не arrow

Нужно `yield` и пошаговая генерация значений?
-> `function*` (generator)

Нужно `await` и асинхронность?
-> `async function`

Нужно создать функцию из строки во время выполнения?
-> Function constructor (с осторожностью)
СпособГде встречается в кодеОсобенность thisnew (конструктор)yield
function name(){}декларацияобычное поведениевозможнонет
const f = function(){}выражениеобычное поведениевозможнонет
const f = () => {}выражениелексическое, своего нетнельзянельзя
obj = { m(){} }, class C{ m(){} }методкак у методанельзятолько у *m(){}
function* g(){}декларацияобычное поведениевозможнода
async function f(){}декларацияобычное поведениезависит от вида функциинет
new Function(...)создание из строкиглобальная областьвозможнонет
В современном JavaScript (ES6+) все перечисленные формы встречаются на практике, но чаще всего используются декларации, выражения, стрелочные функции и методы, а Function(...) оставляется для редких случаев.

Кратко: К основным способам объявления функций относятся function (декларация), function как выражение, стрелочная функция =>, методы в объектах и классах, генераторы function*, асинхронные функции async function и динамическое создание через Function(), при этом Function() следует рассматривать как редкий и потенциально рискованный вариант.