Функции высшего порядка принимают функции в качестве аргумента, либо возвращают функцию как результат.
Вы уже встречали функцию высшего порядка map
:
[1, 2, 3].map(function(x) { return x + 1 });
// [2, 3, 4]
Напишем собственную функцию map
.
function map(array, operator) {
var result = [];
for (var i = 0; i < array.length; i++)
result.push(operator(array[i]));
return result;
}
Вот несколько примеров ее использования:
map([1,2,3,4], function(i) { return i + 4 });
// [5,6,7,8]
map([1,2,3,4], function(i) { return i % 2 == 0 });
// [false, true, false, true]
map(['One', 'Two', 'Three'], function(str) { return str.length });
// [3, 3, 5]
Напишем функцию mapString
, которая принимает строку и функцию-оператор. Оператор применяется к каждой букве строки, чтобы составить результирующий массив.
function mapString(string, operator) {
var result = [];
for (var i = 0; i < string.length; i++) {
// в `operator` передается буква и ее индекс
result.push(operator(string[i], i));
}
return result;
}
mapString('Hello!', function(letter, index) {
return index + ':' + letter
});
// [0:H, 1:e, 2:l, 3:l, 4:o, 5:!]
Функции высшего порядка составляют основу функционального программирования.
В функциональном стиле очень удобно работать со списками, в чем Вы сейчас убедитесь.
С помощью метода filter
можно быстро произвести отсеивание нежелательных элементов:
// Отбираем четные числа из массива
[1,2,3,4,5].filter(function(i) {
return i % 2 == 0;
});
// [2, 4]
[1,2,3,4,5]
// отбираем четные числа
.filter(function(i) { return i % 2 == 0 })
// и умножаем их на 8
.map(function(i) { return i * 8 });
// [16, 32]
Методы every
и some
позволяют установить, выполняется ли заданное условие для всех элементов (every) или хотя бы для одного элемента (some) массива. Условие задается функцией.
// Являются ли все числа четными?
[2,4,6,8].every(function(i) { return i % 2 == 0});
// true
// Есть ли хотя бы одно нечетное число?
[2,4,6,8].some(function(i) { return i % 2 != 0});
// false
Функции высшего порядка, заданные в массивах, не изменяют исходный массив.
var numbers = [1,2,3,4];
var evenNumbers = numbers.filter(function(i) {
return i % 2 == 0;
});
numbers; // [1,2,3,4]
evenNumbers; // [2,4]
Функция имеет побочный эффект, если она изменяет какое-то состояние за пределами собственной области видимости.
var outsideVariable = 'abc';
function a() {
outsideVariable += '1';
return outsideVariable.length % 2 == 0;
}
Здесь функция a
дописывает в строку, объявленную за пределами функции. Это побочный эффект.
Побочными эффектами считаются:
Функции, возвращающие результат, должны быть детерминированными, т.е. свободными от побочных эффектов.
Детерминированные функции гарантируют получение одного и того же результата при вызове их с одними и теми же аргументами.
var oddNumbers = [];
var evenNumbers = [1,2,3,4].filter(function(i) {
if (i % 2 == 0) // возвращаем четные числа, ...
return true;
// а нечетные складываем в другой массив
oddNumbers.push(i);
return false;
});
Такой подход позволяет решить некоторые задачи, но сводит на нет все преимущества функционального программирования.
Не все функции должны быть детерминированными.
Если функция не возвращает результат, то ее единственное назначение — это изменение состояния за ее пределами.
Такие функции принято называть процедурами.