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

## 1. Введение <a href="#vvedenie" id="vvedenie"></a>

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

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

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

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

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

higherOrderFunction(printMessage);
Copy
```

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

## 2. Абстрагирование повторения <a href="#abstragirovanie-povtoreniya" id="abstragirovanie-povtoreniya"></a>

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

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

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

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

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

repeatLog(5);
Copy
```

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

```js
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-функции анонимны и объявляются как стрелочные функции прямо во время передачи.

```js
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. Фильтрация массива <a href="#filtraciya-massiva" id="filtraciya-massiva"></a>

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

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

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

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

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

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

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

  return filteredElements;
};
Copy
```

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

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

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

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

  return filteredElements;
};
Copy
```



{% hint style="info" %}
<mark style="color:blue;">Обратите внимание, функция</mark> <mark style="color:blue;"></mark><mark style="color:blue;">`filter`</mark><mark style="color:blue;">, вместо удаления элементов из оригинального массива, создает новый массив только тех элементов, которые проходят тест. Это - чистая функция, она не изменяет исходные данные.</mark>
{% endhint %}

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

```js
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
```
