functionality - garevna/js-course GitHub Wiki

🎓 Элементы функционального программирования в JS

🎓 Чистая функция - это функция, которая:

  • при одних и тех же значениях аргументов возвращает один и тот же результат ( предсказуемое поведение )
  • не влияет на внешнее окружение, т.е. не изменяет значения внешних переменных ( не создает "побочных эффектов" )
  • не зависит от состояния окружения, потому что не использует внешние переменные в своих вычислениях

🎓 Ссылочная прозрачность ( referential transparency ) - это возможность заменить вызов функции с заданным набором аргументов результатом ее вызова

let func = ( x, y, z ) => x + y - z

Вызов функции func ( 3, 4, 2 ) всегда возвращает 5, поэтому можно заменить func ( 3, 4, 2 ) значением 5

А вот такая функция не будет обладать свойством referential transparency, поскольку результат ее работы будет случайным числом:

let func = ( x, y, z ) => x + y - z * Math.random()

В следующем примере функция counter также не будет ссылочно-прозрачной, поскольку каждый вызов этой функции будет возвращать новый результат:

let makeCounter = () => {
    let counter = 0
    return () => counter++
}

let counter = makeCounter ()

🎓 Иммутабельность ( immutability ) - это устойчивость к изменениям

Иммутабельные данные - это данные, которые не меняются с течением времени

В следующем примере переменные number и sum трижды меняют свое значение в процессе выполнения цикла:

let numbers = [ 10, 5, 7 ]
let sum = 0

for ( let number of array ) {
    sum += number
}

А если использовать функцию summation:

let summation = array => {
    let index = 0, sum = 0
    
    return ( function recurse () {
        sum += array [ index++ ]
        return index < array.length ? recurse () : sum
    })()
} 

то после вызова

let numbers = [ 10, 5, 7 ]
let sum = 0

summation( numbers )  // 22

массив numbers и переменная sum не изменятся, т.е. функция summation не производит внешних эффектов


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

Поскольку функции в JS являются объектами, действовать с ними можно абсолютно так же, как с любым объектом:

  • собирать в структуры ( массивы, объекты )
  • передавать в качестве аргумента
  • возвращать в качестве результата ( что, собственно, и делает конструктор Function )

Одним словом, все, что мы можем делать с объектами, можно делать с функциями, поскольку функции - это всего лишь объекты класса Function

☕ Функция-аргумент
function frog () {
    console.log ( "frog" )
}
function elefant () {
    console.log ( "elefant" )
}

function animal ( func ) {
    func ()
}

В этом примере функция animal получает в качестве аргумента ссылку на функцию, которую и вызывает

Мы можем передавать функции animal различные функции

Вызовем функцию animal:

animal ( frog )
animal ( elefant )

и в консоли мы увидим:

frog
elefant

Однако если аргумент функции animal окажется не функцией, то при попытке его вызова ( с круглыми скобками ) будет сгенерировано исключение ( TypeError )

Во избежание этого немного изменим код функции animal, добавив проверку типа аргумента:

function animal ( func ) {
    typeof func === 'function' ? func () : null
}

Создадим элемент div и добавим его в document.body

Установим свойство id этого элемента равным "sample"

Изменим код функций frog() и elefant():

function frog () {
    return "frog"
}
function elefant () {
    return "elefant"
}

Теперь объявим функцию newFunc(), которая будет создавать новый элемент p, вставлять его в контейнер с идентификатором elemId и устанавливать его содержимое ( innerHTML ) с помощью функции, переданной аргументом:

function newFunc ( func, elemId ) {
    var elem = document.getElementById ( elemId )
    if ( !elem || !elem.nodeType || elem.nodeType !== 1 ) return
    function makeElem () {
        var x = document.createElement ( "p" )
        elem.appendChild ( x )
        x.innerHTML = func ()
        console.log ( x )
    }
    if ( typeof func === 'function' )
        makeElem ( func, elem )
}

Вызовем функцию newFunc, передав ей функцию frog в качестве первого аргумента, а вторым аргументом передадим ей id созданного нами элемента ( "sample" ):

newFunc ( frog, "sample" )

В результате на странице появится новый элемент с текстом, который возвращает функция frog()

Теперь вызовем функцию newFunc(), передав ей функцию elefant():

newFunc ( elefant, "sample" )

В результате на странице появится новый элемент с текстом, который возвращает функция elefant()


☕ Функция-возвращаемое значение

Теперь объявим функцию createFunction(), которая будет возвращать функцию:

function createFunction ( param ) {
    return function () {
        console.log ( 'function ', param )
    }
}

и создадим с ее помощью две новые функции: firstFunc() и secondFunc():

var firstFunc = createFunction ( "First" )
var secondFunc = createFunction ( "Second" )

Вызовем каждую из этих функций:

firstFunc ()
secondFunc ()

В консоли мы увидим:

function  First
function  Second
⚠️ **GitHub.com Fallback** ⚠️