Функции высшего порядка

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

Вы уже встречали функцию высшего порядка map:

[1, 2, 3].map(function(x) { return x + 1 });
// [2, 3, 4]

Пример 1

Напишем собственную функцию map.

function map(array, operator) {
  var result = [];
  for (var i = 0; i < array.length; i++)
    result.push(operator(array[i]));
  return result;
}

Пример 1

Вот несколько примеров ее использования:

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]

Пример 2

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

function mapString(string, operator) {
  var result = [];
  for (var i = 0; i < string.length; i++) {
    // в `operator` передается буква и ее индекс
    result.push(operator(string[i], i));
  }
  return result;
}

Пример 2

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;
});

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

Не все функции должны быть детерминированными.

Если функция не возвращает результат, то ее единственное назначение — это изменение состояния за ее пределами.

Такие функции принято называть процедурами.

Задания для самоподготовки

  • Приведите пример известных Вам функций высшего порядка.
  • Приведите пример функции с побочным эффектом из числа известных Вам.
  • Напишите собственную функцию высшего порядка и вызовите ее.
В начало1 из 20ВыйтиЗавершить просмотр