Vue, реагировать на функциональное программирование

Vue.js React.js

Это 9-й день моего участия в августовском испытании обновлений.Подробности о мероприятии:Испытание августовского обновления

функциональное программирование

Язык JavaScript с самого начала был связан с функциональным программированием. Он рассматривает функции как отдельный тип данных наравне с другими типами данных. В языке JavaScript вы можете использовать объектно-ориентированное программирование или функциональное программирование. Некоторые даже говорят, что JavaScript — это первый функциональный язык программирования, получивший массовое распространение.

Новые функции ES6 делают функциональное программирование проще и мощнее. В этой главе описывается, как ES6 выполняет функциональное программирование.

карри

Каррирование означает разделение многопараметрической функции на ряд функций, каждая из которых принимает только один параметр (унарный).

function add (a, b) {
  return a + b;
}

add(1, 1) // 2

В приведенном выше коде функцияaddпринимает два параметраaа такжеb.

Каррирование разбивает вышеуказанную функцию на две функции, каждая из которых принимает только один параметр.

function add (a) {
  return function (b) {
    return a + b;
  }
}
// 或者采用箭头函数写法
const add = x => y => x + y;

const f = add(1);
f(1) // 2

В приведенном выше коде функцияaddпринимает только один параметрa, который возвращает функциюf. функцияfтакже принимает только один параметрb.

синтез функций

Композиция функций относится к объединению нескольких функций в одну функцию.

const compose = f => g => x => f(g(x));

const f = compose (x => x * 4) (x => x + 3);
f(2) // 20

В приведенном выше кодеcomposeЭто синтезатор функций, который объединяет две функции в одну функцию.

Можно обнаружить, что каррирование тесно связано с композицией функций. Первый используется для разделения функции на несколько функций, а второй используется для объединения нескольких функций в одну функцию.

инверсия параметра

Инверсия аргумента (флип) относится к изменению порядка первых двух аргументов функции.

var divide = (a, b) => a / b;
var flip = f.flip(divide);

flip(10, 5) // 0.5
flip(1, 10) // 10

var three = (a, b, c) => [a, b, c];
var flip = f.flip(three);
flip(1, 2, 3); // => [2, 1, 3]

В приведенном выше коде 10 разделить на 5 равно 2 в обычном порядке параметров. Однако новая функция с перевернутыми аргументами дает результат 5, деленное на 10, что равно 0,5. Если исходная функция имеет 3 параметра, меняются местами только первые два параметра.

Код для инверсии параметров очень прост.

let f = {};
f.flip =
  fn =>
    (a, b, ...args) => fn(b, a, ...args.reverse());

граница исполнения

Граница выполнения (до) относится к выполнению функции до тех пор, пока не будет выполнено условие.

let condition = x => x > 100;
let inc = x => x + 1;
let until = f.until(condition, inc);

until(0) // 101

condition = x => x === 5;
until = f.until(condition, inc);

until(3) // 5

В приведенном выше коде условие первого абзаца должно выполняться до тех пор, покаxболее 100, поэтомуxКогда начальное значение равно 0, оно будет выполняться до 101. Условие второго абзаца выполнять до тех пор, пока оно не станет равным 5, поэтомуxОкончательное значение равно 5.

Реализация границы выполнения выглядит следующим образом.

let f = {};
f.until = (condition, f) =>
  (...args) => {
    var r = f.apply(null, args);
    return condition(r) ? r : f.until(condition, f)(r);
  };

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

операция очереди

Операции с очередью (списком) включают следующее.

  • head: удалить первый непустой член очереди.
  • last: принимает последний непустой член конечной очереди.
  • tail: удалить другие непустые элементы, кроме «головы очереди».
  • init: удалить другие непустые элементы, кроме «хвоста очереди».

Ниже приведен пример.

f.head(5, 27, 3, 1) // 5
f.last(5, 27, 3, 1) // 1
f.tail(5, 27, 3, 1) // [27, 3, 1]
f.init(5, 27, 3, 1) // [5, 27, 3]

Реализация этих способов заключается в следующем.

let f = {};
f.head = (...xs) => xs[0];
f.last = (...xs) => xs.slice(-1);
f.tail = (...xs) => Array.prototype.slice.call(xs, 1);
f.init = (...xs) => xs.slice(0, -1);

операция слияния

Операция слияния делится наconcatа такжеconcatMapдва вида. Первый — объединить несколько массивов в один, а второй — сначала обработать параметры, а затем объединить результаты обработки в массив.

f.concat([5], [27], [3]) // [5, 27, 3]
f.concatMap(x => 'hi ' + x, 1, [[2]], 3) // ['hi 1', 'hi 2', 'hi 3']

Код реализации этих двух методов выглядит следующим образом.

let f = {};
f.concat =
  (...xs) => xs.reduce((a, b) => a.concat(b));
f.concatMap =
  (f, ...xs) => f.concat(xs.map(f));

операция сопряжения

Операции сопряжения делятся наzipа такжеzipWithДва метода.zipОперация объединяет элементы двух очередей один за другим в новую очередь. Если две очереди имеют разную длину, дополнительные элементы из более длинной очереди будут игнорироваться.zipWithПервым параметром операции является функция, а затем следующие члены очереди будут спарены один за другим, войдут в функцию, а возвращаемое значение сформирует новую очередь.

Ниже приведен пример.

let a = [0, 1, 2];
let b = [3, 4, 5];
let c = [6, 7, 8];

f.zip(a, b) // [[0, 3], [1, 4], [2, 5]]
f.zipWith((a, b) => a + b, a, b, c) // [9, 12, 15]

В приведенном выше кодеzipWithПервый параметр метода — это функция суммирования, которая складывает элементы следующих трех очередей по одному, попарно.

Реализация этих двух способов заключается в следующем.

let f = {};

f.zip = (...xs) => {
  let r = [];
  let nple = [];
  let length = Math.min.apply(null, xs.map(x => x.length));

  for (var i = 0; i < length; i++) {
    xs.forEach(
      x => nple.push(x[i])
    );

    r.push(nple);
    nple = [];
  }

  return r;
};

f.zipWith = (op, ...xs) =>
  f.zip.apply(null, xs).map(
    (x) => x.reduce(op)
  );