Урок за JavaScript: Функции от по-висок ред

Миналата седмица небрежно изпуснах термина „функция от по-висок ред“, когато говорих за мемоизиране. Въпреки че сега се чувствам удобно да разхвърлям подобни термини, не винаги знаех какво означават те. Тази седмица ще разгледаме какво представляват функциите от по-висок ред, ще покажем някои често срещани примери и ще се научим как да създадем свои собствени.

В основата си функцията от по-висок ред е просто функция, която приема функция като аргумент или връща функция. Това е възможно в JavaScript благодарение на първокласните функции, което означава, че функциите в JavaScript могат да се предават като всяка друга променлива. Макар това да звучи доста лесно, не е достатъчно телеграфно вида на мощта, която имате с първокласните функции.

Ако пишете JavaScript, вероятно сте използвали функции от по-висок ред и дори не сте забелязали. Ако някога сте замествали forцикъл с метод на масив, сте използвали функции от по-висок ред. Ако някога сте използвали резултатите от AJAX повикване (без async/ await), сте използвали функции от по-висок ред (както обещанията, така и обратните обаждания включват функции от по-висок ред). Ако някога сте писали компонент на React, който показва списък с елементи, използвали сте функции от по-висок ред. Нека да видим тези примери:

const елементи = ['a', 'b', 'c', 'd', 'e']

// Вместо това за цикъл ....

за (нека i = 0; i <items.length - 1; i ++) {

  console.log (елементи [i]);

}

// Можем да използваме forEach, функция от по-висок ред

// (forEach приема функция като аргумент)

items.forEach ((item) => console.log (item));

// Обратни обаждания или обещания, ако правите

// асинхронни заявки, които използвате

// функции от по-висок ред

get ('// aws.random.cat/meow', (response) => {

  putImageOnScreen (response.file);

});

get ('// random.dog/woof.json'). then((response) => {

  putImageOnScreen (response.file);

});

// В React компонента по-долу се използва карта,

// което е функция от по-висок ред

const myListComponent = (подпори) => {

  връщане (

   

          {props.items.map ((item) => {

            връщане (

  • {вещ}
  • )

          })}

      );

    };

Това са примери за функции от по-висок ред, които приемат функции като аргументи, но много от тях връщат и функции. Ако някога сте виждали извикване на функция, която има два набора скоби, това е функция от по-висок ред. Подобни неща бяха по-рядко срещани, но ако изобщо работите с Redux, вероятно сте използвали connectфункцията, която е функция от по-висок ред:

експортиране на свързване по подразбиране (mapStateToProps, mapDispatchToProps) (MyComponent);

В горния случай извикваме connectс два аргумента и той връща функция, която веднага извикваме с един аргумент. Може да сте виждали (или сте писали) и проста библиотека за регистриране, която използва функции като върнати стойности. В примера по-долу ще създадем регистратор, който регистрира контекста си преди съобщението:

const createLogger = (context) => {

  връщане (съобщение) => {

    console.log (`$ {context}: $ {msg}`);

  }

};

const log = createLogger ('myFile');

log ('Много важно съобщение');

// излиза от "myFile: Много важно съобщение"

Примерът по-горе започва да илюстрира част от силата на функциите от по-висок ред (вижте също предишната ми публикация за запомняне). Обърнете внимание, че се createLoggerприема аргумент, който препращаме в тялото на функцията, която връщаме. Върнатата функция, която присвояваме на променливата log, все още може да осъществи достъп до contextаргумента, тъй като беше в обхвата, където функцията беше дефинирана.

Забавен факт: Препращането contextстава възможно чрез затваряне. Тук няма да навлизам в затваряния, защото те заслужават своя публикация, но могат да се използват заедно с функции от по-висок ред за някои наистина интересни ефекти.

Например използването на затваряния заедно с функциите от по-висок порядък беше единственият начин, по който можехме да имаме „частни“ или защитени от промяна променливи в JavaScript:

нека protectedObject = (функция () {

  нека myVar = 0;

  връщане {

    get: () => myVar,

    увеличение: () => myVar ++,

  };

}) ();

protectedObject.get (); // връща 0

protectedObject.increment ();

protectedObject.get (); // връща 1

myVar = 42; // ох! току-що сте създали глобална променлива

protectedObject.get (); // все още връща 1

Нека обаче не се увличаме. Функциите от по-висок ред не изискват нищо изискано като затваряне. Те са просто функции, които приемат други функции като аргументи или връщат функции. Точка. Ако искате повече примери или допълнително четене, вижте главата за функциите от по-висок ред в „Красноречив JavaScript“ от Marijn Haverbeke.

Въпроси или коментари? Чувствайте се свободни да се свържете с Twitter: @freethejazz.