Функции в JavaScript

Функции в JavaScript #

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


Function Declaration #

Самый прямой способ — объявить функцию ключевым словом function:

function formatPrice(amount, currency = 'RUB') {
  return `${amount.toFixed(2)} ${currency}`
}

formatPrice(1490) // "1490.00 RUB"
formatPrice(12.5, 'EUR') // "12.50 EUR"

Объявления «поднимаются» (hoisting): вызов до строки с function в том же scope обычно работает. На практике объявляйте функции до использования — код читается проще.


Параметры и аргументы #

  • Параметры — имена во скобках при объявлении (amount, currency).
  • Аргументы — значения при вызове (1490, 'EUR').

Лишние аргументы игнорируются. Недостающие без значения по умолчанию дают undefined:

function buildQuery(path, page) {
  return `${path}?page=${page}`
}

buildQuery('/catalog') // "/catalog?page=undefined"

Значения по умолчанию и rest-параметры снимают такие сюрпризы:

function buildQuery(path, page = 1, ...filters) {
  const base = `${path}?page=${page}`
  if (filters.length === 0) return base
  return `${base}&filter=${filters.join(',')}`
}

buildQuery('/catalog', 2, 'sale', 'new')
// "/catalog?page=2&filter=sale,new"

return и побочные эффекты #

return завершает выполнение и отдаёт результат наружу. Без return функция возвращает undefined.

function calcDiscount(total, percent) {
  if (percent < 0 || percent > 100) {
    return null // ранний выход при некорректных данных
  }
  return total - (total * percent) / 100
}

const paid = calcDiscount(2000, 15) // 1700

Разделяйте вычисление (чистая функция с return) и побочный эффект (запись в DOM, console.log, запрос к сети). Так проще тестировать и переиспользовать код.


Function Expression #

Функцию можно присвоить переменной — это выражение, не объявление:

const summarizeCart = function (items) {
  return items.reduce((sum, item) => sum + item.price, 0)
}

summarizeCart([
  { title: 'JSFlow Sticker', price: 150 },
  { title: 'Notebook', price: 490 },
]) // 640

У function expression нет hoisting как у declaration: переменная существует, но до присваивания её нельзя вызвать.


Стрелочные функции #

Короткий синтаксис ES2015. Удобны для колбэков и однострочных преобразований:

const toSlug = (title) => title.trim().toLowerCase().replace(/\s+/g, '-')

const scores = [88, 91, 74]
const passed = scores.filter((score) => score >= 80) // [88, 91]

Особенности стрелок:

ТемаПоведение
thisБерётся из внешней области (лексический this)
argumentsНет собственного объекта arguments
newНельзя вызвать как конструктор
телоОдно выражение — неявный return; блок { } — нужен явный return
const double = (n) => n * 2
const describe = (n) => {
  return `value=${n}`
}

Для методов объекта с this чаще берут обычную function или метод в литерале — стрелка «приклеит» чужой this.


Функция как значение #

Функции передают в другие функции (колбэки) и возвращают из функций:

function createTaxed(rate) {
  return function applyTax(net) {
    return net * (1 + rate)
  }
}

const withVat = createTaxed(0.2)
withVat(1000) // 1200

Это основа замыканий, событий и асинхронного кода — отдельные уроки в следующих разделах.


Частые ошибки #

  1. Забыли return — получили undefined вместо результата.
  2. Сравнение с = вместо === внутри условия.
  3. Стрелка как метод объекта — неожиданный this.
  4. Мутация аргументов-объектов без документации — вызывающий код ломается неочевидно.
// плохо: тихо меняем входной объект
function markDone(task) {
  task.done = true
}

// лучше: вернуть новый объект
function markDone(task) {
  return { ...task, done: true }
}

Итог #

  • Функция переиспользует логику с параметрами и return.
  • Declaration, expression и стрелки — разные формы одной идеи; выбирайте по контексту.
  • Чистые функции проще сопровождать, чем смесь расчёта и побочных эффектов.
  • Стрелки удобны в колбэках; для методов с this — обычные функции.

См. также #

Часто спрашивают

Чем let отличается от const?
Оба блочные. let допускает повторное присваивание, const — нет (содержимое объекта при этом можно менять). Подробнее
Зачем нужен use strict?
Строгий режим ловит случайные глобалы и делает this у «голых» вызовов равным undefined; в ES-модулях он включён по умолчанию. Подробнее
Почему лучше писать ===, а не ==?
=== не приводит типы молча; == запускает сложные правила приведения и чаще даёт неожиданный результат. Подробнее