Перейти к содержимому
Теория · Замыкания и this · урок 1 из 11

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

Чему вы научитесь

Чему вы научитесь:

  • передавать функцию аргументом, хранить её в переменной или объекте и возвращать из другой функции;
  • объяснять, что такое замыкание и почему переменная живёт после конца вызова;
  • предсказывать, что вернут функции, созданные в цикле с let и с var;
  • определять, чему равен this в вызове, и возвращать методу его объект через bind или стрелочную функцию.
В машину mapAll вставлена карточка n * 10, и плитки с одной, двумя и тремя точками выходят как 10 20 30

Представьте карточку с рецептом. По ней можно приготовить блюдо прямо сейчас. Но карточку можно и отдать другу, положить в коробку с другими рецептами или прикрепить к холодильнику. Готовить по ней будет тот, у кого она окажется, и тогда, когда ему понадобится.

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

Как вы уже знаете, метод map получает функцию аргументом и сам вызывает её для каждого элемента массива. Функцию, которая работает так же, как map, можно написать и самим. Здесь mapAll делает то же, что map, но массив получает первым параметром:

JavaScript
function mapAll(nums, fn) {
    const result = [];
    for (const n of nums) {
        result.push(fn(n));
    }
    return result;
}

console.log(mapAll([1, 2, 3], (n) => n * 10)); // [ 10, 20, 30 ]
console.log(mapAll([1, 2, 3], (n) => n % 2 === 0)); // [ false, true, false ]

Давайте разберём, что происходит в первом вызове:

  1. mapAll получает массив [1, 2, 3] и стрелочную функцию (n) => n * 10. Функция лежит в параметре fn, как карточка в руке.
  2. Цикл for…of берёт элементы по одному.
  3. Вызов fn(n) выполняет переданную функцию так же, как вызов по имени, и возвращает её результат.
  4. result.push складывает результаты в новый массив, и программа печатает [ 10, 20, 30 ].

Функция высшего порядка

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

Работу здесь делят двое:

  • функция высшего порядка решает, когда и с какими аргументами вызвать колбэк и что сделать с его результатом;
  • колбэк решает, что сделать с одним элементом.

Цикл, который собирает новый массив, написан в mapAll один раз. Для другой задачи меняется только колбэк. В первом вызове он умножает числа на 10, во втором проверяет их на чётность.

Так же устроены forEach, filter и reduce. Обход массива спрятан внутри метода, а действие приходит снаружи.

Что ещё можно делать с функцией

Функция — такое же значение, как число или строка. Раз с функциями можно делать всё то же, что с другими значениями, их называют функциями первого класса. На собеседовании часто спрашивают, что значит «функции в JavaScript — первого класса». Ответ — три действия: функцию можно передать аргументом, сохранить и вернуть из другой функции. Передачу аргументом вы уже видели, осталось два действия.

Сохранить

Функцию можно положить в константу, в элемент массива или в свойство объекта и вызвать оттуда. Это карточка, убранная в коробку: достать её можно в любой момент.

Вернуть

Функцию можно вернуть из другой функции через return. Тогда вызов отдаёт не число и не строку, а готовую карточку. Её можно сохранить, а можно вызвать тут же, поставив после вызова вторые скобки.

Оба действия есть в одном примере:

JavaScript
function pick(sign) {
    if (sign === "+") {
        return (a, b) => a + b;
    }
    return (a, b) => a - b;
}

const ops = { plus: pick("+"), minus: pick("-") };
console.log(ops.plus(5, 3), ops.minus(5, 3)); // 8 2
console.log(pick("+")(2, 2)); // 4

Разберём по шагам:

  1. pick возвращает не число, а функцию: сложение для "+" и вычитание для остальных знаков.
  2. const ops = { … } сохраняет обе функции в свойствах plus и minus объекта ops.
  3. ops.plus(5, 3) достаёт функцию из свойства plus и вызывает её, а программа печатает 8 2.
  4. pick("+")(2, 2) сначала получает функцию сложения, а вторые скобки сразу вызывают её с двумя аргументами. Получается 4.

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

Частая ошибка

Функция должна вернуть функцию, а вернула результат вызова:

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

function getDouble() {
    return double(); // ✗
}

const f = getDouble();
console.log(f(4));
TEXT
TypeError: f is not a function

Скобки после double вызывают её прямо в return, без аргумента, и вызов даёт NaN. Поэтому getDouble возвращает число NaN, а число вызвать нельзя. Это как отдать другу не карточку, а уже приготовленное блюдо. Чтобы вернуть саму функцию, пишите имя без скобок: return double;.

Главное

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

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом