Выбрать главу

function gatherCorrelations(journal) {

  var phis = {};

  for (var entry = 0; entry < journal.length; entry++) {

    var events = journal[entry].events;

    for (var i = 0; i < events.length; i++) {

      var event = events[i];

      if (!(event in phis))

        phis[event] = phi(tableFor(event, journal));

    }

  }

  return phis;

}

Используя forEach мы делаем запись чуть короче и гораздо чище.

function gatherCorrelations(journal) {

  var phis = {};

  journal.forEach(function(entry) {

    entry.events.forEach(function(event) {

      if (!(event in phis))

        phis[event] = phi(tableFor(event, journal));

    });

  });

  return phis;

}

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

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

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

function greaterThan(n) {

  return function(m) { return m > n; };

}

var greaterThan10 = greaterThan(10);

console.log(greaterThan10(11));

// → true

Можно сделать функцию, меняющую другие функции.

function noisy(f) {

  return function(arg) {

    console.log("calling with", arg);

    var val = f(arg);

    console.log("called with", arg, "- got", val);

    return val;

  };

}

noisy(Boolean)(0);

// → calling with 0

// → called with 0 - got false

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

function unless(test, then) {

  if (!test) then();

}

function repeat(times, body) {

  for (var i = 0; i < times; i++) body(i);

}

repeat(3, function(n) {

  unless(n % 2, function() {

    console.log(n, "is even");

  });

});

// → 0 is even

// → 2 is even

Правила лексических областей видимости, которые мы обсуждали в главе 3, работают нам на пользу в таких случаях. В последнем примере переменная n – это аргумент внешней функции. Поскольку внутренняя функция живёт в окружении внешней, она может использовать n. Тела таких внутренних функций имеют доступ к переменным, окружающим их. Они могут играть роль блоков {}, используемых в обычных циклах и условных выражениях. Важное отличие в том, что переменные, объявленные внутри внутренних функций, не попадают в окружение внешней. И обычно это только к лучшему.

Передача аргументов

Функция noisy, объявленная ранее, которая передаёт свой аргумент в другую функцию, не совсем удобна.

function noisy(f) {

  return function(arg) {

    console.log("calling with", arg);

    var val = f(arg);

    console.log("called with", arg, "- got", val);

    return val;

  };

}

Если f принимает больше одного параметра, она получит только первый. Можно было бы добавить кучу аргументов к внутренней функции (arg1, arg2 и т. д.) и передать все их в f, но ведь неизвестно, какого количества нам хватит. Кроме того, функция f не могла бы корректно работать с arguments.length. Так как мы всё время передавали бы одинаковое число аргументов, было бы неизвестно, сколько аргументов нам было задано изначально.

Для таких случаев у функций в JavaScript есть метод apply. Ему передают массив (или объект в виде массива) из аргументов, а он вызывает функцию с этими аргументами.

function transparentWrapping(f) {

  return function() {

    return f.apply(null, arguments);

  };

}

Данная функция бесполезна, но она демонстрирует интересующий нас шаблон – возвращаемая ею функция передаёт в f все полученные ею аргументы, но не более того. Происходит это при помощи передачи её собственных аргументов, хранящихся в объекте arguments, в метод apply. Первый аргумент метода apply, которому мы в данном случае присваиваем null, можно использовать для эмуляции вызова метода. Мы вернёмся к этому вопросу в следующей главе.