Функции в JavaScript: объявление, выражение, стрелки

Содержание

О проверке кода. Примеры не запускались — в отличие от материалов по Node.js с фактическим выводом. Говорим прямо, а не ставим бейдж «проверено».

Функции — сердце JavaScript. Способов объявить их несколько, и разница между ними — не стиль, а поведение. Разберём.

Три способа объявить функцию

// 1. объявление (function declaration)
function add(a, b) {
  return a + b;
}

// 2. выражение (function expression)
const add = function (a, b) {
  return a + b;
};

// 3. стрелочная функция (arrow)
const add = (a, b) => a + b;

Все три складывают два числа, но ведут себя по-разному в трёх вещах: всплытии, this и краткости.

Объявление против выражения: всплытие

// ✅ работает — объявление ВСПЛЫВАЕТ
console.log(sum(2, 3));   // 5
function sum(a, b) { return a + b; }

// ❌ ошибка — выражение НЕ всплывает
console.log(mul(2, 3));   // ReferenceError
const mul = (a, b) => a * b;

Объявление функции всплывает целиком (hoisting) — её можно вызвать в коде выше, чем она написана. Выражение не всплывает: до строки объявления переменная в «мёртвой зоне», как обычная const.

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

Стрелки: краткость и отсутствие своего this

Стрелочные функции — не просто короткая запись. У них два принципиальных отличия.

Краткость нарастает по мере упрощения:

(a, b) => { return a + b; }   // полная форма
(a, b) => a + b               // неявный return — без {} и return
x => x * 2                    // один параметр — без скобок
() => 42                      // без параметров
() => ({ x: 1 })              // вернуть объект — в скобках!

Последняя строка — частая ловушка: () => { x: 1 } вернёт undefined (фигурные скобки поймут как тело), поэтому объект оборачивают в круглые: () => ({ x: 1 }).

Отсутствие своего this — главное. Стрелка берёт this из окружения и не меняет его при вызове:

const timer = {
  seconds: 0,
  start() {
    setInterval(() => this.seconds++, 1000);   // this — это timer ✅
  },
};
Стрелка против обычной функции

Стрелочная

Для колбэков

  • Нет своего this — берёт из окружения
  • Короткая запись, неявный return
  • Нет arguments
  • Нельзя вызвать через new
  • Идеальна как колбэк

Обычная (function)

Для методов и конструкторов

  • Свой this по правилам вызова
  • Есть arguments
  • Можно new
  • Всплывает (если declaration)
  • Метод объекта — только она

Правило: стрелка для колбэков, обычная для методов. Стрелка как метод объекта не сработает — this будет не объектом.

Параметры по умолчанию

// современно
function greet(name = 'гость', greeting = 'Привет') {
  return `${greeting}, ${name}!`;
}
greet();              // "Привет, гость!"
greet('Аня');         // "Привет, Аня!"

Значение по умолчанию подставляется, только если аргумент не передан (undefined). Это заменило старый приём:

// ❌ старый способ — ломается на falsy
function old(count) {
  count = count || 10;   // при count === 0 подставит 10!
}

count || 10 подставляет 10 при любом ложном значении — включая 0 и пустую строку, которые могут быть валидными. Параметры по умолчанию срабатывают строго на undefined, поэтому 0 проходит корректно.

rest и spread: работа с множеством аргументов

// rest — собрать аргументы в массив
function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4);   // 10 — numbers это [1, 2, 3, 4]

// spread — разложить массив в аргументы
const nums = [1, 2, 3];
Math.max(...nums);   // 3

...args в параметрах (rest) собирает все аргументы в настоящий массив — с map, filter, reduce. Это заменило старый псевдомассив arguments, у которого методов массива не было и который работал только в обычных функциях. Про spread и rest подробнее.

Функция — это объект

Последнее и, возможно, самое важное для понимания языка. Функции в JavaScript — значения первого класса: с ними можно всё то же, что с числом или строкой.

const fn = () => 42;          // присвоить переменной
[fn, fn].forEach(f => f());   // положить в массив
setTimeout(fn, 1000);         // передать в другую функцию

function makeAdder(x) {
  return (y) => x + y;        // вернуть функцию из функции
}

Именно потому, что функцию можно передать в другую функцию, работают колбэки, map/filter/forEach, обработчики событий и вся асинхронность. А потому, что её можно вернуть и она помнит окружение, работают замыкания. Это фундамент, на котором стоит функциональный стиль JavaScript.


Смежные темы: this и контекст — почему стрелка для колбэков; замыкания — функции, помнящие окружение; область видимости — про всплытие. Полный список — в уроках JavaScript.

Частые вопросы

Чем function declaration отличается от function expression?
Declaration (function foo(){}) всплывает целиком — её можно вызвать до объявления в коде. Expression (const foo = function(){}) не всплывает: до строки объявления переменная в мёртвой зоне. Практическая разница именно в доступности до объявления.
Когда использовать стрелочную функцию, а когда обычную?
Стрелку — для колбэков и когда нужен this из окружения. Обычную — для методов объекта и конструкторов, где нужен свой this. У стрелки нет своего this, arguments и её нельзя вызвать через new.
Что такое параметры по умолчанию?
Значения, которые подставляются, если аргумент не передан: function greet(name = ‘гость’). Заменили старый приём name = name || ‘гость’, который ломался на falsy-значениях вроде 0 или пустой строки.
Чем rest-параметр отличается от arguments?
rest (…args) — настоящий массив с методами map, filter. arguments — псевдомассив без этих методов, доступный только в обычных функциях. В новом коде берут rest, а arguments считают наследием.
Функция — это объект?
Да. Функции в JavaScript — объекты первого класса: их можно присвоить переменной, передать в другую функцию, вернуть из функции, положить в массив. На этом построен весь функциональный стиль и колбэки.