Как делать11 октября 2026 г.5 мин чтения
JavaScript с нуляШаг 4 из 9

Функции: параметры, возврат, область видимости

Коротко

Функция — именованный блок кода, который принимает данные через параметры, выполняет действия и возвращает результат оператором return; без return она возвращает undefined. В JavaScript функцию можно объявить через function, записать как выражение или как стрелочную функцию (a, b) => a + b. Переменные, объявленные внутри функции, снаружи не видны, а вложенная функция помнит переменные внешней даже после её завершения — это называется замыканием.

Пошаговый урокУровень: НачальныйФункцииПрактика: ~45–90 мин

После материала вы сможете

  • написать функцию тремя способами — объявлением, выражением и стрелочной — и объяснить, чем они отличаются при поднятии
  • задать параметры по умолчанию и rest-параметр и вернуть значение через return
  • объяснить область видимости и замыкание на примере счётчика
  • передать функцию как колбэк в map и filter и разложить скрипт на чистые функции

Что знать заранее

Проверено на

Node.js 24 LTS (24.18.0)Microsoft Edge 155

Сверено с документацией: 11 октября 2026 г.

Содержание
  1. 01Зачем нужны функции
  2. 02Объявление, выражение и стрелочная функция
  3. 03Параметры: значения по умолчанию и rest
  4. 04return и функция без возврата
  5. 05Область видимости и замыкания
  6. 06Функции как значения: колбэки, map и filter
  7. 07Чистые функции и побочные эффекты

Зачем нужны функции

В прошлых уроках расчёт чаевых, скидки и доставки повторялся почти в каждом примере. Каждый повтор — место, где можно ошибиться, и место, которое придётся исправлять, если правило изменится. Функция решает обе проблемы: вы один раз описываете действие, даёте ему имя и дальше вызываете по имени с разными данными. MDN описывает функцию как набор инструкций, который выполняет задачу или вычисляет значение: у функции есть вход и есть результат, связанный со входом.

tip-function.js
function calcTip(bill, percent) {
  return Math.round(bill * percent / 100);
}

console.log(calcTip(12500, 10));   // → 1250
console.log(calcTip(8990, 15));    // → 1349
console.log(calcTip(4000, 5));     // → 200
Один раз описали расчёт — трижды вызвали с разными данными.
  • calcTip — имя функции. Хорошее имя — глагол, который говорит, что функция делает: calcTip, formatPrice, isPaid.
  • bill и percent в скобках — параметры: имена для значений, которые функция получит при вызове.
  • calcTip(12500, 10) — вызов. Числа в скобках — аргументы: 12500 попадает в bill, 10 — в percent.
  • return возвращает результат туда, откуда функцию вызвали, — здесь в console.log.

Объявление, выражение и стрелочная функция

В JavaScript есть три основных способа создать функцию. Результат у них похожий, различаются синтаксис и пара важных деталей.

three-forms.js
// 1. Объявление функции (function declaration)
function toTiyn(tenge) {
  return Math.round(tenge * 100);
}

// 2. Функциональное выражение (function expression)
const toTenge = function (tiyn) {
  return tiyn / 100;
};

// 3. Стрелочная функция: короткая запись без return…
const formatPrice = (tenge) => `${tenge} ₸`;

// …и с телом в фигурных скобках — тогда return обязателен
const priceWithDiscount = (price, percent) => {
  const discount = Math.round(price * percent / 100);
  return price - discount;
};

console.log(toTiyn(1999.9));                 // → 199990
console.log(toTenge(199990));                // → 1999.9
console.log(formatPrice(4990));              // → 4990 ₸
console.log(priceWithDiscount(4990, 10));    // → 4491
Все три формы создают обычные функции, которые вызываются одинаково.

Объявление (declaration)

Как выглядит
function toTiyn(tenge) { … }
Особенности
Поднимается: её можно вызвать выше строки, где она объявлена

Выражение (expression)

Как выглядит
const toTenge = function (tiyn) { … };
Особенности
Функция — значение переменной; вызвать можно только после этой строки

Стрелочная (arrow)

Как выглядит
const formatPrice = (tenge) => …;
Особенности
Короткая запись; без фигурных скобок результат возвращается сам; нет своего this

Поднятие (hoisting) работает только для объявлений. По MDN, интерпретатор поднимает объявление функции целиком в начало области видимости, поэтому вызов выше объявления работает. Функция-выражение и стрелочная функция — это значения переменных const, и до строки объявления их использовать нельзя:

hoisting.js
console.log(double(4));            // → 8

function double(n) {
  return n * 2;
}

console.log(half(4));              // ReferenceError: стрелочная функция ещё не создана

const half = (n) => n / 2;
Первая строка сработает, третья — нет.
terminal
$ node hoisting.js
8
…\first-js\hoisting.js:7
console.log(half(4));              // ReferenceError: стрелочная функция ещё не создана
            ^

ReferenceError: Cannot access 'half' before initialization
    at …

Node.js v24.18.0
Путь сокращён, строки стека опущены. 8 успело вывестись: ошибка случилась во время выполнения.

У стрелочных функций есть ограничения: по MDN, у них нет собственного this и их не используют как методы объектов и конструкторы. В этом уроке это неважно: для коротких расчётов и колбэков стрелочные функции — привычный и удобный выбор.

Параметры: значения по умолчанию и rest

Если при вызове не передать аргумент, параметр получит undefined. Лишние аргументы функция просто не использует. Чтобы не проверять undefined вручную, параметру задают значение по умолчанию прямо в объявлении: name = "гость". По MDN, значение по умолчанию подставляется, только если аргумент не передан или равен undefined, — null считается переданным значением.

params.js
function greet(name = "гость", greeting = "Здравствуйте") {
  return `${greeting}, ${name}!`;
}

console.log(greet("Алия"));              // → Здравствуйте, Алия!
console.log(greet());                    // → Здравствуйте, гость!
console.log(greet(undefined, "Привет")); // → Привет, гость!
console.log(greet(null));                // → Здравствуйте, null!

// rest-параметр собирает все переданные значения в массив
function sum(...prices) {
  let total = 0;
  for (const price of prices) {
    total += price;
  }
  return total;
}

console.log(sum(1600, 1200, 500));       // → 3300
console.log(sum());                      // → 0
greet(null) не подставляет «гость»: null — это значение, а не его отсутствие.

Rest-параметр ...prices собирает все переданные аргументы в массив. Он нужен, когда заранее неизвестно, сколько значений придёт: цен в чеке может быть одна, а может быть двадцать. Rest-параметр может быть только последним в списке.

return и функция без возврата

return делает две вещи: возвращает значение и сразу завершает функцию. Код после выполненного return не выполняется. Этим удобно пользоваться для ранних выходов: сначала отсечь особый случай, потом описать основной.

return.js
function deliveryCost(total) {
  if (total >= 15000) {
    return 0;                // функция заканчивается здесь
  }
  return 1500;
}

function logOrder(id) {
  console.log(`Заказ ${id}`);  // ничего не возвращает
}

console.log(deliveryCost(23500));  // → 0
console.log(deliveryCost(9000));   // → 1500

const result = logOrder(1042);     // → Заказ 1042
console.log(result);               // → undefined
logOrder выводит текст, но ничего не возвращает — результат её вызова равен undefined.

Если в функции нет return или он записан без значения, функция возвращает undefined. Это частый источник ошибок у новичков: функция «что-то посчитала» и вывела в консоль, но в переменную попал undefined. Вывод в консоль и возврат значения — разные действия: console.log показывает значение человеку, return передаёт его дальше в программу.

Область видимости и замыкания

Область видимости (scope) — часть программы, где переменная доступна. По MDN, функция создаёт свою область: переменные, объявленные внутри, снаружи не видны. Блок в фигурных скобках — тело if или цикла — тоже создаёт область для let и const. Изнутри видно всё, что снаружи, но не наоборот.

scope.js
const city = "Алматы";                // глобальная область видимости

function describeOrder(id) {
  const orderStatus = "оплачен";      // видна только внутри функции
  if (id > 1000) {
    const note = "новый клиент";      // видна только внутри блока if
    console.log(`${id}: ${orderStatus}, ${note}, ${city}`);
  }
}

describeOrder(1042);                  // → 1042: оплачен, новый клиент, Алматы
console.log(typeof orderStatus);      // → undefined

// Параметр с тем же именем «затеняет» внешнюю переменную
let total = 100;
function addTip(total) {
  total = total + 10;
  return total;
}
console.log(addTip(50));              // → 60
console.log(total);                   // → 100
Функция видит глобальную city, но orderStatus за пределами функции не существует.
terminal
$ node scope-error.js
…\first-js\scope-error.js:6
console.log(orderId);
            ^

ReferenceError: orderId is not defined
    at …

Node.js v24.18.0
Переменная orderId объявлена внутри createOrder и снаружи недоступна. Путь сокращён, строки стека опущены.

Теперь главное следствие. Функция, созданная внутри другой функции, сохраняет доступ к её переменным — даже когда внешняя функция уже завершилась. MDN определяет замыкание (closure) как сочетание функции и её лексического окружения — переменных, которые были видны в месте её создания.

closure.js
function createCounter() {
  let count = 0;               // живёт, пока жива возвращённая функция
  return () => {
    count++;
    return count;
  };
}

const nextTicket = createCounter();
console.log(nextTicket());     // → 1
console.log(nextTicket());     // → 2

const otherQueue = createCounter();
console.log(otherQueue());     // → 1
console.log(nextTicket());     // → 3

// Функция, которая создаёт функции с «запомненным» параметром
function makeDiscount(percent) {
  return (price) => Math.round(price * (100 - percent) / 100);
}

const studentPrice = makeDiscount(15);
const staffPrice = makeDiscount(30);
console.log(studentPrice(2000)); // → 1700
console.log(staffPrice(2000));   // → 1400
У каждого счётчика своя переменная count; makeDiscount «запоминает» процент.

Каждый вызов createCounter создаёт новую переменную count, и возвращённая функция держит именно её. Поэтому у nextTicket и otherQueue независимые счёта. Изменить count снаружи нельзя: доступ к ней есть только у возвращённой функции. Так замыкания помогают прятать данные и создавать функции «с настройками», как studentPrice и staffPrice.

Функции как значения: колбэки, map и filter

Функция в JavaScript — такое же значение, как число или строка: её можно положить в переменную, передать в другую функцию и вернуть из функции. Функцию, которую передают другой функции, чтобы та её вызвала, называют колбэком (callback). Самые частые примеры — методы массивов filter и map.

callbacks.js
const menu = [
  { name: "Капучино", price: 1600 },
  { name: "Латте", price: 1800 },
  { name: "Американо", price: 1200 },
  { name: "Раф", price: 2200 },
];

// filter оставляет элементы, для которых функция вернула true
const affordable = menu.filter((item) => item.price < 1700);

// map превращает каждый элемент в новый
const names = affordable.map((item) => item.name);
console.log(names.join(", "));          // → Капучино, Американо

// Объект из стрелочной функции — в круглых скобках
const raised = menu.map((item) => ({ ...item, price: item.price + 200 }));
console.log(raised[0].price);           // → 1800
console.log(menu[0].price);             // → 1600

// Свою функцию тоже можно передать как значение
const formatPrice = (tenge) => `${tenge} ₸`;
console.log(menu.map((item) => item.price).map(formatPrice).join(" | ")); // → 1600 ₸ | 1800 ₸ | 1200 ₸ | 2200 ₸
filter и map не меняют исходный массив menu — они создают новые.
  • filter вызывает колбэк для каждого элемента и оставляет те, для которых он вернул истинное значение.
  • map вызывает колбэк для каждого элемента и собирает новый массив из того, что колбэк вернул.
  • Оба метода создают новый массив, а filter по MDN возвращает поверхностную копию: сами объекты внутри — те же.
  • Чтобы вернуть объект из короткой стрелочной функции, оберните его в круглые скобки: (item) => ({ ...item }). Без скобок фигурные скобки будут прочитаны как тело функции.

Чистые функции и побочные эффекты

Побочный эффект — любое действие функции, кроме возврата значения: изменение внешней переменной, вывод в консоль, запись в файл, запрос к серверу. Функцию без побочных эффектов, результат которой зависит только от аргументов, называют чистой. Её проще проверять: одинаковые аргументы всегда дают одинаковый результат.

pure.js
// Нечистая функция: читает и меняет внешнюю переменную
let cartTotal = 0;
function addToCart(price) {
  cartTotal += price;
  return cartTotal;
}

console.log(addToCart(1600));     // → 1600
console.log(addToCart(1600));     // → 3200

// Чистая функция: результат зависит только от аргументов
const addPrice = (total, price) => total + price;

console.log(addPrice(0, 1600));   // → 1600
console.log(addPrice(0, 1600));   // → 1600
addToCart дважды вызвана с одним аргументом и вернула разное; addPrice — всегда одно и то же.

Побочные эффекты нужны — без них программа ничего не покажет и не сохранит. Совет, которому мы следуем в проектах InterLAB: держать расчёты в чистых функциях, а вывод, запросы и запись собирать в немногих местах. Тогда скидку или доставку можно проверить одним вызовом, не запуская всю программу.

С функциями у вас есть все базовые конструкции языка: значения, условия, циклы и способ собирать из них именованные действия. Дальше по траектории — структуры данных: массивы, объекты, Map и Set и их методы. Когда функции начнут работать с сетью и файлами, понадобится урок об обработке ошибок, а замыкания пригодятся в рецепте debounce и throttle.

Как проверено2 · пройденосверено 11 октября 2026 г.
  • запущено, работает

    tip-function, three-forms, params, return, scope, closure, callbacks, pure + решения tip-default, order-functions, queue (RU и EN)

    node 24.18.0: stdout построчно сверен с комментариями // → (check-arrows), как .js и как .mjs; те же файлы выполнены в Microsoft Edge 155 headless — вывод совпал

  • запущено, работает

    hoisting.js, scope-error.js

    node 24.18.0: 8, затем ReferenceError: Cannot access 'half' before initialization; ReferenceError: orderId is not defined; в Edge 155 — те же сообщения с Uncaught. shellsession в статье — реальный вывод с сокращённым путём

«Запущено» — код выполнен во временном проекте или контейнере, вывод в статье взят из прогона. «Частично» — проверено с заглушками вместо внешних сервисов. «Не запускалось» — нужны устройство, аккаунт или ключ; шаги сверены с официальной документацией. Файлы проверочного проекта и логи запусков сохранены в архиве InterLAB.

Расскажите о задаче — мы изучим запрос и свяжемся с вами

Есть задача?
Обсудим и оценим — бесплатно.

Расскажите о продукте или проблеме. Предложим архитектуру, срок и стоимость первой версии после изучения задачи.

  • Оценка объёма и рисков до старта
  • NDA — по вашему или нашему шаблону
  • Старт работ в течение недели

Не присылайте через форму пароли, ключи доступа и исходный код — безопасную передачу доступа обсудим отдельно.

Нажимая кнопку, вы соглашаетесь с обработкой персональных данных.