Функции обратного вызова

1. Введение

В JavaScript функции не отличаются от чисел, строк или массивов - это просто специальный тип данных (объект), который можно хранить в переменной или передавать как аргумент в другую функцию.

Функция высшего порядка — функция, принимающая в качестве параметров другие функции или возвращающая функцию как результат.

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

const printMessage = function (message) {
  console.log(message);
};

const higherOrderFunction = function (callback) {
  const string = 'HOCs are awesome';
  callback(string);
};

higherOrderFunction(printMessage);
Copy

Функции обратного вызова применяются для обработки действий пользователя на странице, при вызове функций к определенному времени, при обработке запросов на сервер и др.

2. Абстрагирование повторения

Абстракция — скрытие деталей реализации. Позволяет думать о задачах на более высоком (абстрактном) уровне.

Функции - это хороший способ построения абстракций. Допустим, скрипт выполняет какое-то действие определенное количество раз. Для этого можно написать цикл for.

for (let i = 0; i < 10; i += 1) {
  console.log(i);
}
Copy

Можем ли мы абстрагировать «делать что-то N раз» как функцию? - да, напишем функцию, которая вызывает console.log() N раз.

const repeatLog = function (n) {
  for (let i = 0; i < n; i += 1) {
    console.log(i);
  }
};

repeatLog(5);
Copy

Но что, если мы хотим сделать что-то, кроме логирования чисел? Поскольку «делать что-то» можно представить как функцию, а функции - это просто значения, мы можем передать действие как аргумент.

const printValue = function (value) {
  console.log(value);
};

const prettyPrint = function (value) {
  console.log('Logging value: ', value);
};

const repeat = function (n, action) {
  for (let i = 0; i < n; i += 1) {
    action(i);
  }
};

// Передаем printValue как callback-функцию
repeat(3, printValue);
// 0
// 1
// 2

// Передаем prettyPrint как callback-функцию
repeat(3, prettyPrint);
// Logging value: 0
// Logging value: 1
// Logging value: 2
Copy

Часто callback-функции анонимны и объявляются как стрелочные функции прямо во время передачи.

const repeat = function (n, action) {
  for (let i = 0; i < n; i += 1) {
    action(i);
  }
};

const labels = [];

repeat(5, value => {
  labels.push(`Label ${value + 1}`);
});

console.log(labels); // ["Label 1", "Label 2", "Label 3", "Label 4", "Label 5"]
Copy

3. Фильтрация массива

Напишем функцию фильтрации элементов массива. Первый параметр array - это массив, будем перебирать его используя цикл for...of.

const filter = function (array) {
  for (const element of array) {
  }
};
Copy

Второй параметр test - это callback-функция, которую будет необходимо выполнить для каждого элемента массива.

const filter = function (array, test) {
  for (const element of array) {
    test(element);
  }
};
Copy

Функция filter создает новый массив, в который будет добавляться результат выполнения callback-функции test над каждым элементом массива array. Результатом работы функции будет массив отфильтрованных элементов.

const filter = function (array, test) {
  const filteredElements = [];

  for (const element of array) {
    test(element);
  }

  return filteredElements;
};
Copy

Если элемент массива проходит тест, то есть callback-функция возвращает true, он добавляться в отфильтрованный массив.

const filter = function (array, test) {
  const filteredElements = [];

  for (const element of array) {
    const passed = test(element);

    if (passed) {
      filteredElements.push(element);
    }
  }

  return filteredElements;
};
Copy

Обратите внимание, функция filter, вместо удаления элементов из оригинального массива, создает новый массив только тех элементов, которые проходят тест. Это - чистая функция, она не изменяет исходные данные.

При таком подходе мы можем выполнять фильтрацию массива по критерию используя всего одну функцию для перебора, а операции над каждым элементом будут зависеть от callback-функции.

const filter = function (array, test) {
  const filteredElements = [];

  for (const element of array) {
    const passed = test(element);

    if (passed) {
      filteredElements.push(element);
    }
  }

  return filteredElements;
};

const fruits = [
  { name: 'apples', quantity: 200, isFresh: true },
  { name: 'grapes', quantity: 150, isFresh: false },
  { name: 'bananas', quantity: 100, isFresh: true },
];

const freshFruits = filter(fruits, fruit => fruit.isFresh);
console.log(freshFruits); // массив с объектами apples и bananas

const fruitsWithQuantity = filter(fruits, fruit => fruit.quantity >= 120);
console.log(fruitsWithAmount); // массив с объектами apples и grapes

Last updated