В JavaScript, функции являются “функциями первого класса”, соответственно они могут:
- храниться в переменной, объекте или массиве.
- передаваться как аргументы в другую функцию.
- и даже могут быть возвращены из функции.
Хранение функций
В JavaScript функции могут храниться тремя способами:
- храниться в переменной:
let fn = function doSomething() {} - храниться в объекте :
let obj = { doSomething : function(){} } - храниться в массиве :
arr.push(function doSomething() {})
В первом и третьем примере я использовал так называемое “функциональное выражение” (Function Expression).
“Функциональные выражения” определяют функцию как часть большего выражения. Это когда строка кода начинается не с ключевого слова function.
В случае, если мы напишем строку с ключевого словаfunction,то это будет “определение функции” (Function Declaration) и к функции будет применятся “поднятие функций”(hoisting).
Функции как аргументы
В следующем примере функцияdoSomethingотправляется в виде аргумента в функциюdoAction().
doAction(function doSomething(){});
doSomething — это “функция обратного вызова”.
Функции обратного вызова передаются в качестве “коллбека”(от “callback“) в качестве аргумента в другие функции. Коллбеки часто встречаемая практика в мире Javascript.
Функции высшего порядка
Функции высшего порядка — это функции, которые принимают в качестве аргументов другие функции, либо возвращают функцию. А также могут делать оба действия одновременно.
Давайте взглянем на встроенные методы, которые как раз являются функциями высшего порядка : filter(), map() и reduce().
filter()вызывается на массиве и принимает функцию в качестве первого аргумента, которая определяет условие фильтрации, которое может быть бесконечно гибким.
let numbers = [1,2,3,4,5,6];
function isEven(x){
return x % 2 === 0;
}
let evenNumbers = numbers.filter(isEven);
//2 4 6
map() превращает список значений в иные(модифицированные вами в переданной функции) значения, не изменяя оригинальный массив.
let numbers = [1,2,3,4,5,6];
function double(x){
return x*2;
}
let doubleNumbers = numbers.map(double);
//2 4 6 8 10 12
reduce() аккумулирует все значения коллекции в единое значение. Второй аргумент может быть любым доступным типом в Javascript.
let numbers = [1,2,3,4,5,6];
function sum(total, value){
return total + value;
}
let total = numbers.reduce(sum, 0);
//21
Как вы можете заметить, работать с коллекциями в функциональном стиле гораздо проще.
А теперь давайте сами создадим функцию высшего порядка doWithLoading(). Она будет принимать функцию в качестве параметра и исполнять её. Перед началом исполнения мы выведем сообщение о начале исполнения, а по завершению о том, что функцию выполнена.
function doWithLoading(fn){
console.log("начало загрузки");
let returnValue = fn();
console.log("конец загрузки");
return returnValue;
}
function process() {
console.log("загружаем...");
}
doWithLoading(process);
// начало загрузки
// загружаем...
// конец загрузки
Функции возвращающие функцию
Ниже пример функции, которая возвращает другую функцию:
function createGenerator(){
return function generateNewID(){}
}
Замыкание
Вот пример функции, createGenerator() которая создает другую функциюgenerateNewID() с приватным состоянием. Каждый раз когда мы вызываем createGenerato,мы запоминаем индекс последнего вызова и возвращаем строку с номером текущего вызова.
function createGenerator(prefix){
let index = 0;
return function generateNewID(){
index++;
return prefix + index.toString();
}
}
let generateNewID = createGenerator("вызов номер: ");
console.log(generateNewID()); //вызов номер: 1
console.log(generateNewID()); //вызов номер: 2
console.log(generateNewID()); //вызов номер: 3
Используя замыкания, теперь мы умеем создавать функции с приватным состоянием.
Декораторы
Декораторы — это функции высшего порядка, которые принимают функцию в виде аргумента и возвращают другую функцию. Возвращаемая функция — это модифицированная функция с аргумента. Но, фишка в том, что мы не изменяем код оригинальной функции.
Самый простой декоратор, который может быть найден в популярной библиотеке, например underscore.js или lodash.js — это функцияonce(). Она кэширует результат вызова функции и при повторном вызов возвращает данные с кэша, не вычисляя их повторно. В мире ООП обычно называется Синглтон. Ниже вы можете увидеть имплементацию данного декоратора.
function once(fn){
let returnValue;
let canRun = true;
return function runOnce(){
if(canRun) {
returnValue = fn.apply(this, arguments);
canRun = false;
}
return returnValue;
}
}
var processonce = once(process);
processonce(); //process
processonce(); //
Функции-декораторы мощная штука для создания произвольного поведения уже существующих функций без изменения кода оных.
Свойства функций
Функция — это объект, поэтому она может иметь свои собственные свойства, например name и length или методыtoString(), bind(), apply() и call().
let log = function logMessage(message) { console.log(message); }
console.log(log.name); //logMessage console.log(log.length); //1 console.log(log.toString());
Частичное применение — это предоставление для функции меньшего количества аргументов, чем она ожидает.
Метод bind() является таким примером. Обратите внимание на первый аргумент, который ожидаетthis.
function log(level, message){}
var logInfo = log.bind(null, "Info");
logInfo("message");
Заметили, как мы создали функциюlogInfo() ? Теперь она принимает только один аргумент(message), минуя this.
Лямбда функции
Лямбда функции — это функции, которые используются как значение.
Eric Elliott in Programming JavaScript Applications
Поскольку в JavaScript, функции являются объектами первого класса, их можно использовать как лямбда функции. Лямбда функции могут быть именованные и безымянные. Мне кажется, что вам привычней использовать именованные функции.
Ниже пример renderFirstItem функции использованной как лямбда:
function renderFirstItem(){
/* code */
}
$("#first").click(renderFirstItem);
Заключение
Функции первого класса и Замыкания, концепции которые предоставляют возможность писать в функциональном стиле. В результате мы можем использовать функции высшего порядка для удобной работы с массивами, а также использовать их в виде декораторов.
Также функции первого класса позволяют нам абстрагировать некий функционал и работать с функцией как с примитивом.
В JavaScript функции тоже объекты. А объекты могут иметь методы, например call, apply, bind,которые могут использовать частичное применение функций чтобы установить контекст this.
Лучшая вещь в JavaScript — это имплементация функций. Это дает возможность делать всё правильно.
Douglas Crockford in JavaScript The Good Parts
Перевод статьи Cristi Salcescu: Discover the power of first class functions





