Стрелочные функции и замыкания
Зачем это нужно
В JavaScript функция — это значение, такое же, как число или строка. Её можно положить в
переменную, передать в другую функцию, вернуть из функции. На этом построено очень многое: обработчики
кликов, методы массивов map и filter (следующие темы), таймеры, запросы к серверу. А короткая
стрелочная запись делает такой код компактным и читаемым.
Функциональное выражение
Функцию можно создать и положить в переменную:
const greet = function (name) {
return `Привет, ${name}!`;
};
console.log(greet("Аня"));
Вывод:
Привет, Аня!
Это функциональное выражение. Отличие от объявления function greet() {} — его можно вызвать
только после строки, где оно создано.
Стрелочные функции
Та же функция короче:
const greet = (name) => {
return `Привет, ${name}!`;
};
А если тело — одно выражение, фигурные скобки и return можно убрать: результат вернётся сам.
const greet = (name) => `Привет, ${name}!`;
const add = (a, b) => a + b;
const random = () => Math.random(); // без параметров — пустые скобки
const square = n => n * n; // один параметр — скобки можно опустить
console.log(add(2, 3), square(5));
Вывод:
5 25
Осторожно с объектами: чтобы вернуть объект в короткой форме, его берут в круглые скобки —
() => ({ ok: true }). Иначе фигурные скобки примут за тело функции.
Функции как аргументы: колбэки
Функцию, которую передают в другую функцию, чтобы та её вызвала, называют колбэком (callback — «перезвони»):
function repeat(times, action) {
for (let i = 1; i <= times; i++) {
action(i);
}
}
repeat(3, (n) => console.log("Повтор №" + n));
Вывод:
Повтор №1
Повтор №2
Повтор №3
Функция repeat не знает, что делать, — она знает только сколько раз. А что делать, решает тот,
кто её вызывает. Так устроены setTimeout(колбэк, мс), addEventListener("click", колбэк) и методы массивов.
Функции, которые возвращают функции
function multiplier(k) {
return (n) => n * k;
}
const triple = multiplier(3);
const tenTimes = multiplier(10);
console.log(triple(5), tenTimes(5));
Вывод:
15 50
multiplier — «фабрика» функций: каждый вызов создаёт новую функцию со своим k.
Замыкания
Обрати внимание: triple вызывается после того, как multiplier уже закончила работу. Откуда
она помнит k = 3? Функция в JavaScript запоминает переменные того места, где её создали.
Это называется замыкание (closure).
function makeGreeter(greeting) {
let calls = 0;
return (name) => {
calls++;
return `${greeting}, ${name}! (раз ${calls})`;
};
}
const hi = makeGreeter("Привет");
console.log(hi("Аня"));
console.log(hi("Тимур"));
Вывод:
Привет, Аня! (раз 1)
Привет, Тимур! (раз 2)
Переменная calls живёт, пока жива функция hi, и при этом спрятана: снаружи её не изменить.
Так в JS делают «приватные» данные.
Как это работает внутри
Каждый вызов функции создаёт окружение — набор её переменных. Обычно после выхода из функции
окружение выбрасывается. Но если внутри создали другую функцию и вернули её наружу, та хранит ссылку
на окружение, и оно продолжает жить. Каждый новый вызов makeCounter() создаёт новое окружение —
поэтому два счётчика не мешают друг другу.
У стрелочных функций нет своего this и prototype — поэтому их не используют как конструкторы
и как методы объектов, которым нужен this (об этом — в теме про объекты).
Частые ошибки
- Фигурные скобки без
return.(n) => { n * 2 }возвращаетundefined. Либо убери скобки, либо допишиreturn. - Вызов вместо передачи.
repeat(3, sayHi())передаёт результат вызова. Нужноrepeat(3, sayHi). - Объект без скобок.
() => { a: 1 }— это тело функции, а не объект. Нужно() => ({ a: 1 }). - Общая переменная вне фабрики. Если
let count = 0написать снаружиmakeCounter, все счётчики будут делить одно число. - Вызов выражения до создания.
const f = …нельзя вызвать выше этой строки.
Шпаргалка
| Что | Как |
|---|---|
| Выражение | const f = function (x) { return x; }; |
| Стрелка с телом | const f = (x) => { return x * 2; }; |
| Короткая стрелка | const f = (x) => x * 2; |
| Без параметров | () => 42 |
| Вернуть объект | () => ({ ok: true }) |
| Колбэк | repeat(3, (i) => console.log(i)) |
| Замыкание | функция помнит переменные места, где создана |