Функции: как код перестаёт повторяться
Зачем это нужно
Пока программа умещается в двадцать строк, функции кажутся лишней церемонией. Проблема начинается на сотой строке: одно и то же вычисление комиссии оказывается скопированным в четырёх местах, а поменять формулу надо во всех четырёх — и в одном из них ты про неё забудешь.
Функция — это способ дать куску кода имя, чтобы дальше пользоваться именем, а не куском. Всё остальное — следствие: код становится короче, изменение делается в одном месте, а имена функций начинают работать как документация. Читать feeFor(size) понятнее, чем size * 0.001 + 0.0005, даже если формула перед глазами.
В блокчейн-коде это заметнее, чем где-либо. Подписать транзакцию, посчитать комиссию, дождаться подтверждения, разобрать ответ ноды — каждое из этих действий пишут один раз и вызывают из десятка мест. А ещё функции здесь передают как значения: «сделай вот это, когда придёт ответ» — это функция, отданная другой функции.
Глава разбирает функции полностью: как объявляются, чем три формы записи отличаются друг от друга, что такое подъём, замыкание и this, почему стрелочные функции ведут себя иначе, и какие проверки добавляет TypeScript. Все выводы и сообщения об ошибках сняты с живого запуска Node.js 24 и TypeScript 6.
- Зачем это нужно
- Анатомия функции: от объявления до вызова
- Параметры и аргументы: два разных слова
return: что функция отдаёт наружу- Значения по умолчанию, необязательные параметры и остаток
- Три формы записи и чем они различаются
- Подъём: почему одна форма работает раньше объявления, а другая — нет
- Область видимости и замыкания
- Функция как значение: обратные вызовы
- Функция, возвращающая функцию
thisи почему стрелки ведут себя иначе- Чистые функции и побочные эффекты
- Рекурсия и предел стека
- Что добавляет TypeScript: сигнатура функции
- Необязательные параметры и проверка на пустоту
- Как называть функции и сколько делать параметров
- Типичные ошибки: текст, перевод, причина, починка
- Тренажёр: функции вслепую
- Символы этой главы
- Что должен уметь
- Куда дальше: проверенные ресурсы
Анатомия функции: от объявления до вызова
Самая простая функция целиком:
function fee(size) {
return size * 0.001;
}
console.log(fee(1000)); // 1
Первые три строки — объявление: здесь функция описана, но ничего не выполняется. Четвёртая — вызов: здесь она работает.
И вызов:
Написать имя без скобок — не ошибка, а другое действие: получится сама функция как значение. Проверить легко живым запуском:
console.log(typeof fee); // function
console.log(fee(1000)); // 1
Первая строка печатает «function», а не число, потому что вызова не было. Эта разница пригодится через несколько разделов, когда функции начнут передавать как значения.
Параметры и аргументы: два разных слова
Слова похожие, и путать их не стоит, потому что дальше в сообщениях об ошибках встретятся оба.
- Параметр — имя в объявлении. Существует только внутри функции, снаружи о нём никто не знает.
- Аргумент — конкретное значение при вызове.
function pair(a, b) { return [a, b]; } // a и b — параметры
pair(1, 2); // 1 и 2 — аргументы
Параметры сопоставляются с аргументами по порядку, а не по имени. Первый аргумент попадает в первый параметр, второй — во второй. Никакой связи с именами нет: переставишь аргументы местами — функция молча посчитает не то.
Чистый JavaScript при этом на количество не смотрит вообще. Живой запуск:
console.log(pair(1)); // [ 1, undefined ]
console.log(pair(1, 2, 3)); // [ 1, 2 ]
Недостающий аргумент даёт undefined, лишний просто отбрасывается. Ошибки нет ни там, ни там — и это одна из главных причин, по которым в блокчейн-проектах пишут на TypeScript: там оба случая ловятся до запуска.
error TS2554: Expected 2 arguments, but got 1.
error TS2554: Expected 2 arguments, but got 3.
Перевод: «ожидалось 2 аргумента, получено 1» и «…получено 3».
return: что функция отдаёт наружу
return делает две вещи одновременно: отдаёт значение в точку вызова и немедленно завершает функцию. Код после return в той же ветке не выполнится никогда.
function label(status) {
if (status === 'confirmed') {
return 'в блоке';
console.log('это никогда не напечатается');
}
return 'ещё нет';
}
Функция без return возвращает undefined — не ноль, не пустую строку, не ошибку. Живой запуск:
function nothing() {}
console.log(nothing()); // undefined
Отсюда классическая ловушка: функция что-то печатает, но ничего не возвращает, а результат её вызова присваивают переменной. В переменной оказывается undefined, и падает всё дальше по цепочке, далеко от настоящей причины.
return без значения (return;) тоже возвращает undefined — его пишут, когда нужно просто выйти из функции досрочно.
Второй важный момент: вернуть можно ровно одно значение. Нужно несколько — упаковывают в массив или объект:
function analyze(tx) {
return { valid: tx.amount > 0, fee: tx.amount * 0.001 };
}
const { valid, fee } = analyze(tx);
Отдельная беда — return на своей строке. Из-за автоматической расстановки точек с запятой запись
return
{ valid: true };
превращается в return;, и функция вернёт undefined, а объект окажется недостижимым кодом. Ошибки не будет. Правило: значение всегда начинается на той же строке, что и return.
Значения по умолчанию, необязательные параметры и остаток
Три приёма, которые делают функции удобнее.
Значение по умолчанию подставляется, если аргумент не передали:
function feeFor(size, rate = 0.001) { return size * rate; }
console.log(feeFor(1000)); // 1
console.log(feeFor(1000, 0.002)); // 2
Значение по умолчанию вычисляется при каждом вызове и может ссылаться на предыдущие параметры: function f(a, b = a * 2) — законная запись.
Остаточные параметры собирают всё лишнее в массив:
function sum(...nums) { return nums.reduce((a, b) => a + b, 0); }
console.log(sum(1, 2, 3)); // 6
console.log(sum()); // 0
Три точки перед именем — это оператор остатка. Такой параметр может быть только один и только последним. Обрати внимание: sum() без аргументов вернул 0, а не ошибку, — nums оказался пустым массивом.
Объект вместо длинного списка параметров. Когда параметров становится больше трёх, вызов вида send('3P...', 5, 0.001, true, false) невозможно прочитать: что означает последний false, знает только автор. Решение — передавать объект и разбирать его прямо в списке параметров:
function send({ to, amount, fee = 0.001 }) { return `${to} ${amount} ${fee}`; }
console.log(send({ to: '3P...', amount: 5 })); // 3P... 5 0.001
Фигурные скобки в списке параметров — деструктуризация: из пришедшего объекта достаются поля to, amount, fee, причём у последнего есть значение по умолчанию. Вызов теперь читается сам: каждое значение подписано именем.
Три формы записи и чем они различаются
Одну и ту же функцию можно записать тремя способами. Разница не только в буквах.
// 1. Объявление функции
function fee(size) { return size * 0.001; }
// 2. Функциональное выражение
const fee2 = function (size) { return size * 0.001; };
// 3. Стрелочная функция
const fee3 = (size) => size * 0.001;
Стрелочную форму разберём подробно — в современном коде она встречается чаще всех:
У стрелки два варианта тела, и путаница между ними — очень частая ошибка:
const short = (x) => x * 2; // тело-выражение: результат возвращается сам
const long = (x) => { return x * 2; }; // тело-блок: return обязателен
const wrong = (x) => { x * 2; }; // фигурные скобки есть, return нет → вернёт undefined
Отдельный случай — вернуть объект. Фигурная скобка после стрелки понимается как начало блока, поэтому объект оборачивают в круглые скобки:
const objRet = (x) => ({ v: x });
console.log(JSON.stringify(objRet(2))); // {"v":2}
Практическое правило выбора: function — для функций верхнего уровня, стрелка — для коротких вставок внутри других вызовов (map, filter, обработчики). Функциональные выражения в форме const f = function () {} в новом коде почти не пишут — их вытеснили стрелки.
Подъём: почему одна форма работает раньше объявления, а другая — нет
Живой запуск, который объясняет разницу лучше любой теории:
console.log(fee(10)); // 0.01 — работает!
function fee(x) { return x * 0.001; }
console.log(arrow(1)); // падает
const arrow = (x) => x + 1;
ReferenceError: Cannot access 'arrow' before initialization
Перевод: «нельзя обратиться к arrow до инициализации».
Причина в подъёме (hoisting). Объявления функций через function обрабатываются до выполнения кода: к моменту первой строки функция уже существует целиком. Переменная через const или let тоже «известна» с начала блока, но до своей строки находится в мёртвой зоне — обратиться к ней нельзя, и язык сообщает об этом честной ошибкой.
Практический вывод простой: пиши объявления выше использования — и вопрос подъёма перестанет тебя касаться. А ошибку Cannot access … before initialization теперь узнаешь с первого взгляда: где-то стрелочная функция вызвана раньше, чем объявлена.
Область видимости и замыкания
Всё, что объявлено внутри функции, снаружи не существует. Это и есть область видимости: имена живут в том блоке, где созданы.
function calc() {
const inside = 42;
return inside;
}
console.log(inside); // ReferenceError: inside is not defined
Обратное неверно: изнутри функция видит имена снаружи. И вот тут начинается самое интересное — функция запоминает окружение, в котором была создана, даже когда это окружение уже отработало:
function counter() {
let n = 0;
return () => ++n;
}
const c = counter();
console.log(c(), c(), c()); // 1 2 3
Функция counter давно завершилась, а n жива и растёт: возвращённая стрелка держит её при себе. Такая пара «функция плюс запомненное окружение» называется замыканием (closure).
Замыкания — не экзотика, ты уже пользовался ими в главе про циклы: разница между var и let в цикле — ровно про то, какое окружение запомнили три созданных функции. И почти каждый обработчик, переданный в map или в ожидание ответа ноды, — замыкание: он видит переменные того места, где написан.
Функция как значение: обратные вызовы
Функция в JavaScript — обычное значение. Её можно положить в переменную, в поле объекта, в массив, передать другой функции и вернуть из функции. Живой запуск:
const ops = { double: (x) => x * 2 };
console.log(typeof ops.double, ops.double(21)); // function 42
Функция, переданная другой функции, называется обратным вызовом (callback): «вот тебе кусок работы, вызови его сам, когда понадобится». Ты уже видел это в map, filter, forEach. Ещё один типичный пример — сортировка:
const txs = [{ id: 'a', amount: 5 }, { id: 'b', amount: 1 }, { id: 'c', amount: 9 }];
console.log(txs.sort((x, y) => x.amount - y.amount).map((t) => t.id).join('')); // bac
Метод sort не знает, как сравнивать транзакции, — и не должен. Он получает функцию сравнения и вызывает её столько раз, сколько нужно.
Самая частая ошибка при передаче обратного вызова — лишние скобки:
setTimeout(check, 1000); // правильно: передали функцию
setTimeout(check(), 1000); // неправильно: вызвали немедленно и передали её результат
Во втором случае check выполнится прямо сейчас, а через секунду setTimeout попытается вызвать то, что она вернула. Если это undefined — ничего не произойдёт. Молча.
Функция, возвращающая функцию
Обратная ситуация: функция отдаёт наружу другую функцию, заранее «настроенную».
const withRate = (rate) => (size) => size * rate;
const feeMain = withRate(0.001);
console.log(feeMain(1000)); // 1
console.log(withRate(0.01)(1000)); // 10
Читается справа налево: withRate принимает ставку и возвращает функцию, которая принимает размер. Две пары скобок подряд в последней строке — это два вызова: сначала получили функцию, потом сразу её вызвали.
Приём выглядит вычурно, пока не столкнёшься с задачей «одна и та же операция, но с разной настройкой»: клиент для тестовой и основной сети, логгер с разным префиксом, проверка с разным порогом. Вместо того чтобы таскать настройку в каждый вызов, её фиксируют один раз.
Внутри здесь работает всё то же замыкание: возвращённая функция помнит rate.
this и почему стрелки ведут себя иначе
Единственное принципиальное отличие стрелочных функций от обычных — поведение this. Живой запуск в строгом режиме:
'use strict';
const node = {
name: 'узел-1',
regular() { return this.name; },
arrow: () => (typeof this === 'undefined' ? 'this отсутствует' : String(this && this.name)),
};
console.log(node.regular()); // узел-1
console.log(node.arrow()); // undefined
const f = node.regular;
f(); // падает
console.log(f.call(node)); // узел-1
TypeError: Cannot read properties of undefined (reading 'name')
Разбор трёх результатов:
- Обычный метод сработал. У обычной функции
thisопределяется в момент вызова и указывает на то, что стоит слева от точки: вызвалиnode.regular()—thisсталnode. - Стрелка не увидела объект. У стрелочной функции своего
thisнет вообще; она берёт его из места, где написана, — а написана она на верхнем уровне модуля, где никакого объекта нет. - Метод, оторванный от объекта, упал. В
fпопала сама функция, слева от точки при вызове ничего нет,thisв строгом режиме сталundefined, и обращение к его полю далоTypeError. Методcallпозволяет указатьthisявно — так и получилось вернуть имя.
Отсюда рабочее правило: методы объектов и классов пиши обычными функциями, короткие вставки внутри них — стрелками. Именно потому, что стрелка не заводит свой this, обработчик внутри метода видит тот же this, что и сам метод, — а с обычной функцией пришлось бы городить const self = this или bind.
TypeError: Cannot read properties of undefined (reading 'name') — самое частое сообщение в JavaScript вообще. В контексте функций оно почти всегда означает одно: метод оторвали от объекта. Классический случай — передали obj.method как обратный вызов вместо () => obj.method().
Чистые функции и побочные эффекты
Функция называется чистой, если она зависит только от своих аргументов и ничего не меняет снаружи. Одинаковые аргументы — всегда одинаковый результат.
function fee(size) { return size * 0.001; } // чистая
Нечистая делает что-то ещё — меняет внешнюю переменную, пишет в файл, ходит в сеть. Живой запуск показывает, чем это неудобно:
let total = 0;
function impure(x) { total += x; return total; }
console.log(impure(5), impure(5)); // 5 10
Один и тот же вызов дал разные ответы. Такую функцию нельзя переставить местами с соседней, нельзя вызвать дважды «на всякий случай», нельзя проверить в отрыве от остальной программы, а ошибку в ней ищут по всему файлу.
Полностью без побочных эффектов программа бесполезна: рано или поздно надо отправить транзакцию и напечатать результат. Приём в другом — отделять вычисление от действия. Пусть считает чистая функция, а отправляет отдельная. Тогда сложную часть можно проверить одним вызовом, без сети и без ноды.
Рекурсия и предел стека
Функция может вызвать саму себя. Классический пример — факториал:
function fact(n) { return n <= 1 ? 1 : n * fact(n - 1); }
console.log(fact(5)); // 120
В рекурсии обязательны две части: условие остановки (n <= 1) и шаг, приближающий к остановке (n - 1). Забыть любую — получить бесконечную рекурсию, и она, в отличие от бесконечного цикла, падает довольно быстро:
RangeError: Maximum call stack size exceeded
Перевод: «превышен максимальный размер стека вызовов». У каждой незавершённой функции есть место в памяти — кадр стека; их количество ограничено примерно десятком тысяч. Это сообщение — надёжный признак рекурсии без условия остановки (или с условием, до которого не доходит).
Рекурсия естественна там, где данные вложены сами в себя: дерево блоков, разбор JSON, обход каталогов. Для простого перебора списка почти всегда проще и быстрее обычный цикл.
Что добавляет TypeScript: сигнатура функции
В TypeScript у функции появляется сигнатура — типы параметров и типа результата:
function fee(size: number, rate: number): number {
return size * rate;
}
Что это даёт на практике — четыре живые ошибки компилятора:
error TS2554: Expected 2 arguments, but got 1.
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
error TS2322: Type 'number' is not assignable to type 'string'.
error TS7006: Parameter 'x' implicitly has an 'any' type.
Построчно: не хватило аргумента; передали строку вместо числа; вернули число там, где обещана строка; у параметра не указан тип и вывести его неоткуда. Последняя — про режим strict, и именно она заставляет писать типы параметров: там, где TypeScript не может вывести тип сам, он требует его указать.
Тип результата чаще всего писать не обязательно — компилятор выведет его из return. Но у функций, которые ты отдаёшь наружу, его пишут явно: тогда ошибка появится в самой функции, а не у того, кто ей воспользовался.
Особый тип результата — void: «функция ничего не возвращает». Попытка что-то вернуть даст:
error TS2322: Type 'number' is not assignable to type 'void'.
Тип бывает и у самой функции целиком:
const h: (a: number) => number = (a: string) => 1;
error TS2322: Type '(a: string) => number' is not assignable to type '(a: number) => number'.
Types of parameters 'a' and 'a' are incompatible.
Запись (a: number) => number — это тип функции: принимает число, возвращает число. Стрелка здесь не создаёт функцию, а описывает её форму. Такой тип пишут, когда функцию передают параметром: function retry(action: () => Promise<void>, times: number).
Необязательные параметры и проверка на пустоту
Знак вопроса после имени параметра делает его необязательным:
function greet(name?: string) {
return `привет, ${name ?? 'аноним'}`;
}
Два правила, которые компилятор проверяет сам.
Первое: необязательные параметры идут последними.
error TS1016: A required parameter cannot follow an optional parameter.
Перевод: «обязательный параметр не может следовать за необязательным». Логично: аргументы сопоставляются по порядку, и пропустить средний нельзя.
Второе: внутри функции необязательный параметр может отсутствовать, и это надо учесть.
function b(name?: string) { return name.length; }
error TS18048: 'name' is possibly 'undefined'.
Перевод: «name, возможно, undefined». Чинится проверкой, значением по умолчанию или оператором ??. Это ровно то сужение типа, о котором была речь в главе про условия: после if (!name) return 0; компилятор перестаёт ругаться, потому что дальше name точно строка.
Запись name?: string и name: string = 'аноним' решают похожие задачи, но по-разному: первая допускает undefined внутри, вторая подставляет значение и внутри функции всегда даёт строку. Если есть разумное значение по умолчанию — второй вариант удобнее.
Как называть функции и сколько делать параметров
Правила короткие, но экономят часы.
Имя — глагол или ответ на вопрос. fetchBalance, signTx, isConfirmed, feeFor. Функция, возвращающая логическое значение, начинается с is, has или can — тогда if (isConfirmed(tx)) читается как предложение. Имена вроде data, process, handle не говорят ничего.
Одна функция — одно дело. Если в описании функции появляется слово «и» («считает комиссию и отправляет транзакцию»), это две функции. Проверяется просто: такую функцию невозможно назвать коротко.
Параметров — до трёх. Дальше передавай объект: вызов станет самодокументированным, а порядок перестанет иметь значение.
Логическое значение параметром — плохая идея. В вызове send(tx, true) смысл true не восстановить, не открыв объявление. Либо объект с именованным полем, либо две отдельные функции.
Размер. Универсального предела нет, но функция, не помещающаяся на экран, почти всегда делает больше одного дела. Это не эстетика: читать чужой код на чемпионате приходится под таймером, и короткие функции с честными именами дают понять программу без чтения тел.
Типичные ошибки: текст, перевод, причина, починка
| Сообщение | Перевод | Что произошло | Как чинить |
|---|---|---|---|
ReferenceError: Cannot access 'arrow' before initialization | Обращение до инициализации | Стрелочная функция вызвана выше своего объявления | Перенести объявление выше вызова |
TypeError: fee is not a function | fee не функция | Имя занято не функцией, либо опечатка в имени, либо забыт импорт | Проверить typeof fee и написание имени |
TypeError: Cannot read properties of undefined (reading 'name') | Чтение поля у undefined | Метод оторвали от объекта — this стал undefined | Передавать () => obj.method() вместо obj.method |
RangeError: Maximum call stack size exceeded | Переполнение стека вызовов | Рекурсия без условия остановки | Добавить условие выхода и шаг к нему |
error TS2554: Expected 2 arguments, but got 1 | Ожидалось 2 аргумента | Пропущен аргумент при вызове | Передать все обязательные или сделать параметр необязательным |
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number' | Строку нельзя передать вместо числа | Перепутан порядок аргументов или тип значения | Проверить порядок и типы аргументов |
error TS7006: Parameter 'x' implicitly has an 'any' type | Неявный any у параметра | У параметра не указан тип, вывести неоткуда | Дописать тип параметра |
error TS1016: A required parameter cannot follow an optional parameter | Обязательный после необязательного | Порядок параметров нарушен | Переставить необязательные в конец |
error TS18048: 'name' is possibly 'undefined' | Возможно undefined | Необязательный параметр используют без проверки | Проверка, ?? или значение по умолчанию |
error TS2322: Type 'number' is not assignable to type 'void' | Числа нельзя вернуть из void | Функция объявлена как ничего не возвращающая, но возвращает | Исправить тип результата или убрать значение из return |
Функция вернула undefined, ошибки нет | — | Забыт return, либо значение перенесено на строку ниже return | Проверить, что значение стоит на одной строке с return |
| Обратный вызов сработал сразу | — | При передаче написали check() вместо check | Убрать скобки или обернуть в стрелку |
Тренажёр: функции вслепую
Цель: скорость ≥ 110 зн/мин, точность ≥ 90%
function feeFor(size, rate = 0.001) { return size * rate; }
Символы этой главы
| Знак | Название | Что делает |
|---|---|---|
function | ключевое слово | объявляет функцию; такое объявление поднимается наверх |
( ) после имени | скобки вызова | именно они запускают функцию; без них имя — просто значение |
=> | стрелка | краткая форма записи функции; без фигурных скобок возвращает результат сама |
=> ({ … }) | круглые скобки вокруг объекта | нужны, чтобы фигурная скобка не была понята как начало блока |
... перед параметром | остаточные параметры | собирают все лишние аргументы в массив |
= в списке параметров | значение по умолчанию | подставляется, когда аргумент не передан |
{ } в списке параметров | деструктуризация | разбирает пришедший объект на именованные поля |
? после имени параметра | необязательный параметр | значение может отсутствовать; только в TypeScript |
: внутри скобок | тип параметра | только в TypeScript |
: после скобок | тип результата | только в TypeScript |
(a: number) => number | тип функции | описывает форму функции, а не создаёт её |
void | тип «ничего не возвращает» | попытка вернуть значение даст ошибку |
Что должен уметь
- Объявлять функцию и вызывать её, объясняя, что делают скобки.
- Отличать параметр от аргумента и помнить, что сопоставление идёт по порядку.
- Помнить, что чистый JavaScript не проверяет число аргументов, а TypeScript проверяет.
- Пользоваться
returnи объяснять, почему код после него не выполняется. - Знать, что функция без
returnвозвращаетundefined, и узнавать эту ловушку по симптому. - Задавать значения по умолчанию, собирать остаток через
...и передавать объект вместо длинного списка параметров. - Записывать функцию тремя способами и объяснять разницу между телом-выражением и телом-блоком у стрелки.
- Возвращать объект из стрелки, не забывая круглые скобки.
- Объяснять подъём и узнавать
Cannot access … before initialization. - Объяснять замыкание на примере счётчика.
- Передавать функцию как значение и не ставить лишние скобки при передаче обратного вызова.
- Писать функцию, возвращающую функцию, и понимать две пары скобок подряд.
- Объяснять разницу
thisв обычной и стрелочной функции и узнавать оторванный от объекта метод. - Отличать чистую функцию от нечистой и отделять вычисление от действия.
- Писать рекурсию с условием остановки и узнавать
Maximum call stack size exceeded. - Читать и писать сигнатуру: типы параметров, тип результата,
void, тип функции. - Пользоваться необязательными параметрами и понимать
TS1016иTS18048. - Называть функции глаголами и держать число параметров в пределах трёх.
Углубиться
bind, call, apply. Три метода, позволяющие задать this вручную. call(obj, a, b) вызывает функцию немедленно с указанным this, apply(obj, [a, b]) — то же самое, но аргументы массивом, bind(obj) ничего не вызывает, а возвращает новую функцию с намертво привязанным this. До появления стрелок bind стоял в каждом втором обработчике; сейчас его в основном узнают в чужом коде.
Стрелка не имеет не только this. У стрелочных функций нет ни arguments, ни prototype, их нельзя вызвать через new и нельзя использовать как генератор. Это не ограничения, а следствие: стрелка задумана как короткая вставка, а не как самостоятельный объект.
arguments. В обычных функциях доступен псевдомассив всех переданных аргументов — живой запуск даёт old(1, 2, 3).length === 3. Приём старый: в новом коде вместо него пишут остаточные параметры ...args, у которых настоящий массив и понятный тип.
Перегрузка в TypeScript. Одну функцию можно описать несколькими сигнатурами: сначала объявления без тела, затем одна реализация. Компилятор подберёт подходящую по типам аргументов. Приём нужен, когда функция ведёт себя по-разному в зависимости от типа входа, — например, принимает и строковый идентификатор, и готовый объект.
Стоимость вызова. Каждый вызов создаёт кадр стека; кадров конечное число, отсюда RangeError. Некоторые языки умеют хвостовую оптимизацию — если рекурсивный вызов стоит последним, кадр переиспользуется. В стандарте JavaScript такая оптимизация описана, но движки её в основном не реализуют, поэтому глубокую рекурсию в вебе переписывают циклом.
Функции высшего порядка. Общее название для функций, принимающих или возвращающих функции. map, filter, sort, setTimeout, withRate — все они из этой семьи. Термин встретится в любой статье про функциональный подход, и за ним не стоит ничего сложнее того, что уже разобрано в этой главе.
Дальше по теме:
- MDN: функции — полное руководство от первоисточника
- MDN: стрелочные функции — все отличия в одном месте
- MDN: замыкания — с примерами и разбором частых ошибок
- TypeScript Handbook: More on Functions — сигнатуры, перегрузка, типы функций
Сводная проверка по всей главе: 15 вопросов, 5:00 на всё, без подсказок по ходу. Вопросы показываются по одному, ответ изменить нельзя. Если время выйдет — экзамен завершится с тем, что успел ответить. Пересдавать можно сколько угодно раз.
Куда дальше: проверенные ресурсы
-
MDN: функции — полное руководство — объявления, выражения, параметры, область видимости.
-
MDN: справочник по функциям — все формы записи и их тонкости.
-
MDN: стрелочные функции — отличия от обычных функций списком.
-
MDN: замыкания — с разбором частых ошибок.
-
MDN: параметры по умолчанию — как вычисляются и когда подставляются.
-
MDN: остаточные параметры — три точки в списке параметров.
-
MDN: this — все правила определения this в одном месте.
-
MDN: Function.prototype.bind — привязка this и частичное применение.
-
TypeScript Handbook: More on Functions — сигнатуры, перегрузка, типы функций.
-
Видео и материалы сообщества — ниже — ролики по теме и ссылки от студентов и преподавателей смотри в самом низу страницы.
Комментарии
Обсуждение живёт в GitHub Discussions и грузится с чужого домена — поэтому только по нажатию.