Функции высшего порядка в JavaScript – объяснение на практических примерах

Как веб-разработчик, вы всегда должны стремиться изучать новые методы и открывать способы более эффективной работы с JavaScript.

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

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

Что такое функция высшего порядка?

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

Существует несколько типов функций высшего порядка, таких как отображение и уменьшение. Мы обсудим это позже в настоящем руководстве. Но перед этим давайте поначалу глубже погрузимся в то, что такое функции высшего порядка.

// Callback function, passed as a parameter in the higher order function
function callbackFunction(){
    console.log('I am  a callback function');
}

// higher order function
function higherOrderFunction(func){
    console.log('I am higher order function')
    func()
}

higherOrderFunction(callbackFunction);

В приведенном выше коде higherOrderFunction() это HOF, поскольку мы передаем ему функцию обратного вызова как параметр.

Приведенный выше пример достаточно прост, не правда ли? Итак, давайте расширим его дальше и посмотрим, как вы можете использовать HOF для написания более лаконичного и модульного кода.

Как работают функции высшего порядка

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

const radius = [1, 2, 3];
// function to calculate area of the circle
const calculateArea =  function (radius) {
    const output = [];
    for(let i = 0; i< radius.length; i++){
        output.push(Math.PI * radius[i] * radius[i]);
    }
    return output;
}
// function to calculate diameter of the circle
const calculateDiameter =  function (radius) {
    const output = [];
    for(let i = 0; i< radius.length; i++){
        output.push(2 * radius[i]);
    }
    return output;
}
console.log(calculateArea(radius));
console.log(calculateDiameter(radius))

Но заметили ли вы проблему с приведенным выше кодом? Не пишем ли мы почти ту же функцию снова и снова с несколько иной логикой? Кроме того, функции, которые мы написали, нельзя использовать повторно, не правда ли?

Итак, давайте посмотрим, как мы можем написать тот же код с помощью HOF:

const radius = [1, 2, 3];
// logic to clculate area
const area = function(radius){
    return Math.PI * radius * radius;
}
// logic to calculate diameter
const diameter = function(radius){
    return 2 * radius;
}
// a reusable function to calculate area, diameter, etc
const calculate = function(radius, logic){ 
    const output = [];
    for(let i = 0; i < radius.length; i++){
        output.push(logic(radius[i]))
    }
    return output;
}
console.log(calculate(radius, area));
console.log(calculate(radius, diameter));

Теперь, как вы видите в коде выше, мы написали только одну функцию calculate() вычислить площадь и диаметр круга. Нам нужно только написать логику и передать ее calculate() и функция выполнит свою работу.

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

Предположим, что в будущем мы захотим написать программу для вычисления окружности круга. Мы можем просто написать логику вычисления окружности и передать ее в calculate() функция.

const circumeference = function(radius){
    return 2 * Math.PI * radius;
}
console.log(calculate(radius, circumeference));

Как использовать функции высшего порядка

Вы можете использовать функции высшего порядка разными способами.

При работе с массивами можно использовать map(), reduce(), filter()и sort() функции обработки и преобразования данных в массиве.

При работе с объектами можно использовать Object.entries() функция создания нового массива из объекта.

При работе с функциями можно использовать compose() функция для создания сложных функций из более простых.

Как использовать некоторые важные функции высшего порядка

Есть разные встроенные HOF, и некие из самых распространенных это map(), filter() и reduce(). Итак, давайте разберемся в каждом из них поподробнее.

Как использовать map() в JavaScript

The map() функция принимает массив значений и применяет преобразование к каждому значению в массиве. Это не изменяет оригинальный массив. Его часто используют для преобразования массива данных в новый массив с другой структурой.

Разберемся на примерах.

Пример 1: Предположим, мы хотим добавить 10 к каждому элементу в массиве. Мы можем использовать map() метод отображения каждого элемента в массиве, чтобы добавить к нему 10.

const arr = [1, 2, 3, 4, 5];
const output = arr.map((num) => num += 10)
console.log(arr); // [1, 2, 3, 4, 5]
console.log(output); // [11, 12, 13, 14, 15]

В приведенном выше примере arr это массив из пяти элементов: 1, 2, 3, 4 и 5. map это метод, который мы используем для применения функции к каждому элементу в массиве, и возвращает новый массив с измененными элементами.

Передаваемая функция обратного вызова map использует синтаксис функции стрелки и принимает один аргумент num. Эта функция добавляет 10 к num (каждый элемент в массиве) и возвращает результат.

Пример 2: Здесь у нас есть ряд пользователей. Предположим, что нам нужны только имя и фамилия. Мы можем просто использовать map() метод извлечения его из users массив.

const users = [
    {firstName: 'John', lastName: 'Doe', age: 25},
    {firstName: 'Jane', lastName: 'Doe', age: 30},
    {firstName: 'Jack', lastName: 'Doe', age: 35},
    {firstName: 'Jill', lastName: 'Doe', age: 40},
    {firstName: 'Joe', lastName: 'Doe', age: 45},
]

const result = users.map((user) => user.firstName + ' ' + user.lastName)
console.log(result); // ['John Doe', 'Jane Doe', 'Jack Doe', 'Jill Doe', 'Joe Doe']

В приведенном выше коде users это массив объектов, представляющих юзеров. Каждый объект имеет три свойства: firstName, lastNameи age.

Мы отображаем каждого пользователя с помощью map() метод извлечения свойств firstName и lastName.

Функция обратного вызова принимает один аргумент user который представляет элемент в users массив(объект).

Функция объединяет firstName и lastName свойства user и возвращает результат.

Как использовать filter() в JavaScript

The filter() функция принимает массив и возвращает новый массив только со значениями, отвечающими определенным критериям. Он также не изменяет исходный массив. Его часто используют для выбора подмножества данных из массива на основе определенных критериев.

Пример 1: Вы можете использовать filter() чтобы вернуть только нечетные числа из массива чисел.

const arr = [1, 2, 3, 4, 5];
const output = arr.filter((num) => num % 2) // filter out odd numbers
console.log(arr); // [1, 2, 3, 4, 5]
console.log(output); // [1, 3, 5]

В приведенном выше коде arr это массив из пяти элементов: 1, 2, 3, 4 и 5. filter это метод, используемый для создания нового массива с элементами, проходящими тест, указанный в предоставленной функции обратного вызова.

Эта функция обратного вызова проверяет, num является нечетным путем проверки, не делится ли оно на 2 (num % 2). Если num не делится на 2, функция возвращает trueиначе возвращается false.

Когда filter вызывается arrон применяет эту функцию к каждому элементу в массиве, создавая новый массив только с вернувшимися элементами. true или передал указанное условие при передаче в функцию. Оригинальный arr остается неизменным и возвращает результат.

Пример 2: Вы можете использовать filter() чтобы вернуть в массиве только пользователей старше 30 лет.

const users = [
    {firstName: 'John', lastName: 'Doe', age: 25},
    {firstName: 'Jane', lastName: 'Doe', age: 30},
    {firstName: 'Jack', lastName: 'Doe', age: 35},
    {firstName: 'Jill', lastName: 'Doe', age: 40},
    {firstName: 'Joe', lastName: 'Doe', age: 45},
]

// Find the users with age greater than 30
const output = users.filter(({age}) => age > 30)
console.log(output); // [{firstName: 'Jack', lastName: 'Doe', age: 35}, {firstName: 'Jill', lastName: 'Doe', age: 40}, {firstName: 'Joe', lastName: 'Doe', age: 45}]

В приведенном выше коде users это массив объектов, представляющих юзеров. Каждый объект имеет три свойства: firstName, lastNameи age.

filter называется на users массив и применяет функцию обратного вызова к каждому элементу в массиве.

Функция принимает один аргумент, деструктурированный объект к одному свойству age. Эта функция проверяет, age больше 30. Если это так, функция возвращает trueиначе возвращается false.

Когда filter вызывается usersон применяет эту функцию к каждому элементу в массиве, создавая новый массив только с вернувшимися элементами. true при передаче функции и возвращает результат. Оригинальный users массив остается неизменным.

Как использовать reduce() в JavaScript

The reduce() метод вроде бы потрясающий. Если вы наткнулись reduce() метод раньше и поначалу не понял, это вполне нормально.

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

У вас может возникнуть сомнение, почему мы используем reduce() метод. Поскольку существует много методов, как мы можем решить, какой из них использовать и когда?

В случае с reduce() Вы должны его использовать, если вы хотите выполнить определенную операцию над элементами массива и вернуть единое значение как результат. Единое значение относится к накопленному результату многократного применения функции к элементам последовательности.

Например, вы можете использовать reduce() чтобы подытожить все элементы в массиве, найти максимальное или минимальное значение, объединить несколько объектов в один объект или сгруппировать разные элементы в массиве.

Теперь разберемся во всем этом на примерах.

Пример 1: Использование reduce() чтобы подытожить все элементы в массиве:

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce((total, currentValue) => {
    return total + currentValue;
}, 0)

console.log(sum); // 15

В этом примере reduce() метод называется на numbers массив и передается функция обратного вызова, принимающая два аргумента: total и currentValue.

The total аргумент – это накопление значений, которые были возвращены функцией на данный момент, и currentValue текущий обрабатываемый элемент массива.

The reduce() В этом случае метод также принимает исходное значение как второй аргумент 0которое используется как первоначальное значение total для первой итерации

В каждой итерации функция добавляет текущее значение в итог и возвращает новое значение итога.

The reduce() метод затем использует возвращенное значение как total для последующей итерации пока он не обработает все элементы в массиве.

Наконец он возвращает окончательное значение total, которое является суммой всех элементов в массиве.

Пример 2: Использование reduce() чтобы найти максимальное значение в массиве:

let numbers = [5, 20, 100, 60, 1];
const maxValue = numbers.reduce((max, curr) => {
    if(curr > max) max = curr;
    return max;
});
console.log(maxValue); // 100

В этом примере мы снова имеем два аргумента max и curr в функции обратного вызова Обратите внимание, что мы не передали второй параметр в reduce() в этот раз. Следовательно, значением по умолчанию будет являться первый элемент в массиве.

Функция обратного вызова сначала проверяет, текущий ли элемент curr больше текущего максимального значения max. Если да, значение обновляется max быть текущим элементом. Если это не так, max не обновляется. Наконец, функция возвращает значение max.

В этом случае reduce() метод начнется с настройки max до 5 и curr до 20. Затем он проверит, есть ли 20 больше 5, поэтому он обновляется max до 20

Затем он установится curr до 100 и проверьте, 100 больше 20, то есть оно обновляется max до 100

Он продолжит этот процесс до тех пор, пока не обработает все элементы в массиве. Окончательное значение max будет максимальным значением в массиве, которое в данном случае равно 100.

Пример 3: Использование reduce() чтобы объединить разные объекты в один объект:

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const obj3 = { e: 5, f: 6 };
const mergedObj = [obj1, obj2, obj3].reduce((acc, curr) => {
    return { ...acc, ...curr };
}, {});
console.log(mergedObj); // { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }

В этом примере у нас есть два аргумента acc и curr в функции обратного вызова The acc представляет текущий объединенный объект, который был создан на данный момент, в то время как curr представляет текущий объект, обрабатываемый в массиве.

Функция использует оператор распространения (...), чтобы создать новый объект, объединяющий свойства текущего объединенного объекта acc и текущий объект curr. Затем он возвращает этот новый объект.

В этом случае reduce() метод начнется с настройки acc к пустому объекту (это значение, переданное как второй аргумент reduce()). Затем он установится curr к obj1и создайте новый объект, объединяющий свойства пустого объекта и obj1. Затем он установится curr к obj2 и создать новый объект, сочетающий в себе свойства предыдущего объединенного объекта и obj2. Он продолжит этот процесс, пока не обработает все объекты в массиве.

Окончательное значение acc будет объединенным объектом, содержащим все свойства оригинальных объектов.

Пример 4: Использование reduce() группировать объекты в массив. Например, группировка продуктов в корзине в соответствии с их торговой маркой.

const shoppingCart = [
    {name: 'Apple', price: 1.99, quantity: 3},
    {name: 'Apple', price: 1.99, quantity: 3},
    {name: 'Xiomi', price: 2.99, quantity: 2},
    {name: 'Samsung', price: 3.99, quantity: 1},
    {name: 'Tesla', price: 3.99, quantity: 1},
    {name: 'Tesla', price: 4.99, quantity: 4},
    {name: 'Nokia', price: 4.99, quantity: 4},
]

const products = shoppingCart.reduce((productGroup, product) => {
    const name = product.name;
    if(productGroup[name] == null) {
        productGroup[name] = [];
    }
    productGroup[name].push(product);

    return productGroup;
}, {});

console.log(products);
// OUTPUT
{
  Apple: [
    { name: 'Apple', price: 1.99, quantity: 3 },
    { name: 'Apple', price: 1.99, quantity: 3 }
  ],
  Xiomi: [ { name: 'Xiomi', price: 2.99, quantity: 2 } ],
  Samsung: [ { name: 'Samsung', price: 3.99, quantity: 1 } ],
  Tesla: [
    { name: 'Tesla', price: 3.99, quantity: 1 },
    { name: 'Tesla', price: 4.99, quantity: 4 }
  ],
  Nokia: [ { name: 'Nokia', price: 4.99, quantity: 4 } ]
}

В этом примере мы имеем shoppingCart массив, представляющий разные продукты и два аргумента productGroup и product в функции обратного вызова

The productGroup аргумент представляет текущую группу продуктов, найденных на данный момент, тогда как product аргумент представляет текущий обрабатываемый продукт в массиве.

Функция сначала получает название текущего продукта, использующего product.name. Затем он проверяет, существует ли группа для этого названия продукта в productGroup объект с помощью if заявление. Если нет, создается новая группа путем инициализации productGroup[name] свойства к пустому массиву.

Наконец, функция выталкивает текущий продукт в группу с помощью push() и возвращает обновленный productGroup объект.

После reduce() метод обработал все элементы в shoppingCart массив, полученный productGroup будет содержать ключи для каждого названия продукта и значения, которые являются массивами продуктов с таким названием.

Преимущества функций высшего порядка

Использование функций высшего порядка имеет некоторые важные преимущества для веб-разработчиков.

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

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

Вывод

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

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

Теперь, когда вы пытаетесь использовать методы map(), filter() и reduce() и запутаетесь, просто помните следующее:

  • Используйте map, когда вы хотите превратить массив
  • Используйте фильтр для выбора подмножества данных из массива и
  • Используйте уменьшение, если вы хотите вернуть одно значение как результат.

Для получения дополнительных сведений о функциях высшего порядка просмотрите это замечательное видео Акшая Саини на YouTube.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *