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

Чистые функции — функция, которая не имеет побочных эффектов, работает со своими собственными переменными и состоянием или со всем, что ей передается (не изменяет внешнее состояние).

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

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

вот пример составления двух чистых функций вместе

function sum( x, y ) {
return x + y;
}

function mult( x, y ) {
return x * y;
}

var z = mult( 3, 4 );
z = sum( z, 5 );

z; // 17

в этой функции есть состояние побочного эффекта, z изменяется, что может вызвать сложность в вашей отладке

Чтобы перекомпоновать это, чтобы вам не пришлось повторно назначать переменную

function sum( x, y ) {
return x + y;
}

function mult( x, y ) {
return x * y;
}

// 5 + ( 3 * 4 )
sum( mult(3, 4), 5) // 17

мы также могли бы создать чистую оболочку для завершения ручной композиции

function multAndSum( x, y, z ){
return sum( mult( x, y ), z);
}

// 5 + ( 3 * 4 )
multAndSum( 3, 4, 5 ); // 17

Неизменяемость

вар х = 2;
х++; // позволил

const у = 3;
у++; // не допускается!

заблуждение состоит в том, что const дает вам неизменяемые значения…
ключевое слово const дает вам неизменное назначение или привязку к значению…
y не может быть присвоено никакому другому значению, мы ничего не говорим о природе ценить себя.

Примером этого может быть:

const z = [ 4, 5, 6 ];
z = 10; // нельзя!
z[0] = 10; // позволил!

const только говорит, что всегда будет указывать на определенный массив, но значения в массиве могут меняться

чтобы полностью «заблокировать» значение, которое можно было бы использовать:

const w = Object.freeze([ 4, 5, 6]);
w = 10; // не разрешено;
w[0] = 10; // не допускается;

Учтите это при создании функции, которая принимает массив:

function doubleThemMutable(list) {
for (let i = 0; i < list.length; i++){
list[i] = list[i] * 2;
}
}
let arr = [ 3, 4, 5 ];
doubleThemMutable(arr);
arr; // [ 6, 8, 10 ]

этот подход довольно распространен, и в нем нет ничего плохого, но он нечист. Это означает, что он изменяет исходный массив на месте (изменяемый). Чтобы сделать это неизменным, мы хотели бы, чтобы он создавал другой массив.

function doubleThemImmutable(list) {
let newList = [];
for (let i = 0; i < list.length; i++){
newList[i] = list[i] * 2;
}
return newList;
}
let arr = [ 3, 4, 5 ];
let arr2 = doubleThemMutable(arr);

arr; // [ 3, 4, 5 ]
arr2; //[ 6, 8, 10]

не имеет значения, является ли массив на самом деле неизменяемым, важно то, что мы используем принцип неизменности, чтобы не изменять обр.

Замыкание — сохранение состояния внутри функции —

Закрытие — это когда функция «запоминает» окружающие ее переменные, даже если эта функция выполняется в другом месте.

это интересное приложение:

function foo() {
var count = 0
return function() {
return count++
};
}

var x = foo();
x(); //0
x(); //1
x(); //2

в этом примере мы поддерживаем состояние в функции foo(). Хотя концептуально это «замыкание», на самом деле это нечистая функция, потому что каждый раз, когда мы вызываем x, мы получаем другой ответ (побочный эффект).

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

пример:

function sumX (x) {
return function(y) {
return x + y
}
}

var add10 = sumX(10)

add10(3); // 13
add10(14); // 24

в этом примере значение x никогда не меняется, и это более функциональный способ использования замыкания для составления функции. X всегда будет установлено значение 10 в этой функции.

Рекурсия (функция, которая вызывает сама себя и останавливается, когда достигает базового случая)

function sumRecur(){
var args = [].slice.call(arguments);
if(args.length <= 2) {
return args[0] + args[1];
} return ( args[0] + sumRecur.apply(null, args.slice(1))
);
}

параметры передаются, аргументы назначаются … args — это синтаксис остальных параметров — собирает аргументы и помещает их в реальный массив (в контексте присваивания) — устраняет необходимость в
var args = [].slice.call(arguments);

вышеуказанная функция станет

function sumRecur(...args){
if(args.length <= 2) {
return args[0] + args[1];
}
(here it spreads the arguments into the array, no need for apply)
return ( args[0] + sumRecur(...args.slice(1))
);
} sumRecur(3, 4, 5);

=====

…args будет обрабатывать произвольное количество аргументов
function multi(…args){
if(args.lengths <= 2) {
return args[0] * args[1];
}
return args [0] * mult(…args.slice(1));
}
//slice не меняет исходный массив, он возвращает новый массив
mult(3, 4, 5); //60 mult(3, 4, 5, 6); //?

Преобразование списка (карта)

берет одно значение и что-то делает с ним, чтобы получить другое значение. список имеет то же количество значений, что и исходный список, сопоставление 1: 1 исходного значения с новым значением — карта создает совершенно новый массив

function doubleIt(v) { return v * 2; } [ 1, 2, 3, 4, 5 ].map(doubleIt); // [ 2, 4, 6, 8, 10]

Исключение из списка (фильтр)

keep some remove some смотрит на само значение и решает, сохранять его или нет
function isOdd(v) { return v % 2 == 1 } [ 1, 2, 3, 4, 5 ].filter(isOdd); // [ 1, 3, 5 ]