Способы объявления функций в 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 (с осторожностью)
| Способ | Где встречается в коде | Особенность this | new (конструктор) | yield |
|---|---|---|---|---|
function name(){} | декларация | обычное поведение | возможно | нет |
const f = function(){} | выражение | обычное поведение | возможно | нет |
const f = () => {} | выражение | лексическое, своего нет | нельзя | нельзя |
obj = { m(){} }, class C{ m(){} } | метод | как у метода | нельзя | только у *m(){} |
function* g(){} | декларация | обычное поведение | возможно | да |
async function f(){} | декларация | обычное поведение | зависит от вида функции | нет |
new Function(...) | создание из строки | глобальная область | возможно | нет |
Function(...) оставляется для редких случаев.Кратко: К основным способам объявления функций относятся function (декларация), function как выражение, стрелочная функция =>, методы в объектах и классах, генераторы function*, асинхронные функции async function и динамическое создание через Function(), при этом Function() следует рассматривать как редкий и потенциально рискованный вариант.