SoFunction
Updated on 2025-03-10

Introduction to JavaScript functional programming implementation

Why learn functional programming

After Vue entered the 3.* (One Piece) generation, the setup grammar introduced is quite similar to the big brother React. Maybe the front-end will really be a framework in the world in the future. Back to the topic, the trend of frameworks is indeed that the developers' js skills are more stringent. Whether it is hooks or setup, they are inseparable from functional programming, which can reuse logic from code, and better organize and reuse code. One thing I am very happy about is that I can finally abandon the annoying this. Of course, this is not because I have such a feeling for being lazy. Mr. Douglas complained about this in his new book "JavaScript Enlightenment", so he can be regarded as a js boss. Therefore, if you want to avoid being overwhelmed by the ever-changing new technologies at the front end, you should come back and learn JavaScript again at the right time.

What is functional programming

Functional Programming (FP), FP is one of the programming paradigms. The programming paradigms we often hear about include process-oriented programming and object-oriented programming.

Object-oriented programming: Object-oriented has three major characteristics, which demonstrates the connection between things through encapsulation, inheritance and polymorphism. If it is more broadly, abstraction should also be included, but since the essence of object-oriented is abstraction, it is not an exaggeration to consider it as three major characteristics.

Functional programming: The idea of ​​functional programming is mainly to abstract the operation process. It is more like a black box. You give a specific output and return the operation result after entering the black box operation. You can understand it as y = f(x) in mathematics.

  • The essence of a program: perform some operation based on the input to obtain the corresponding output.
  • x -> f(contact, mapping) -> y, y = f(x)
  • Functions in functional programming actually correspond to functions in mathematics, that is, mapping relationships.
  • The same input always gets the same output (pure function)
  • Reusable

Pre-knowledge

Function is a first-class citizen

As a front-end developer with some experience, you must be familiar with the statement that "functions are first-class citizens" in JavaScript.

Here is the definition of the authoritative document MDN: when a function of a programming language can be used as a variable, it is said that the language has first-class functions. For example, in this language, a function can be passed as a parameter to another function, as a return value of another function, and can also be assigned to a variable.

Functions can be stored in variables

let fn = function() {
  ('Hello First-class Function')
}
fn()

Functions as parameters

function foo(arr, fun) {
  for (let i = 0; i < ; i++) {
    fun(arr[i])
  }
}
const array = [1, 2, 3, 4]
foo(array, function(a) { (a) })

Function as return value

function fun() {
  return function () {
    ('Hahaha')
  }
}
const fn = fun()
fn()

Advanced functions

What is a higher-order function

Advanced functions

  • You can pass a function as a parameter to another function
  • You can use a function as the return result of another function

Functions are used as parameters (in order to avoid the article being too long, the subsequent demonstration code will not give the test code. Readers can copy the article code and debug it on the local editor)

function filter(array, fn) {
    let results = []
    for (let i = 0; i &lt; ; i++) {
        if (fn(array[i])) {
            (array[i])
        }
    }
    return results
}
// testlet arr = [1, 3, 4, 7, 8]
const results = filter(arr, function(num) {
    return num &gt; 7
})
(results) // [8]

Function as return value

// Consider a scenario where in the case of network delay, if the user clicks to pay, you must not want the user to click on the next payment and then pay again after clicking on the next payment, otherwise your company will be not far from going bankrupt.// So consider the once functionfunction once(fn) {
    let done = false
    return function() {
        if (!done) {
            done = true
            return (this, arguments)
        }
    }
}
let pay = once(function (money) {
    (`Pay: ${money} RMB`)
})
pay(5)
pay(5)
pay(5)
pay(5)
// 5

The meaning of using higher-order functions

  • Abstraction can help us block details, just focus on the goal
  • Higher-order functions are used to abstract general problems

Commonly used higher-order functions

  • forEach (Implemented)
  • map
const map = (array, fn) => {
  let results = []
  for (let value of array) {
    (fn(value))
  }
  return results
}
  • filter
  • every
const every = (array, fn) => {
  let result = true
  for (let value of array) {
    result = fn(value)
    if (!result) {
      break
    }
  }
  return result
}
  • some
const some = (array, fn) => {
  let result = false
  for (let value of array) {
    result = fn(value)
    if (result) {
      break
    }
  }
  return result
}
  • find/findIndex
  • reduce
  • sort

Closure

Closure: A function is bundled with references to its surrounding state (lexical environment) to form a closure.

The essence of closure: When executing, the function will be placed on an execution stack. When the function is executed, it will be removed from the execution stack. However, the scope members on the heap cannot be released because they are referenced externally, so the internal function can still access the members of the external function.

function makePower(power) {
    return function (num) {
        return (num, power)
    }
}
// Find squares and cubicslet power2 = makePower(2)
let power3 = makePower(3)
(power2(4)) // 16
(power2(5)) // 25
(power3(4)) // 64
function maekSalary(base) {
    return function (performance) {
        return base + performance
    }
}
let salaryLevel1 = makeSalary(12000)
let salaryLevel2 = makeSalary(15000)
(salaryLevel1(2000)) // 14000
(salaryLevel2(3000)) // 18000

In fact, the above two functions are similar, both by maintaining references to the internal members of the original function. You can learn more about it through the browser debugging tool.

Pure functions

Pure Function Concept

Pure functions: the same input will always get the same output

lodash is a pure function library that provides some methods for operating arrays, numbers, objects, strings, functions, etc. Some people may have such doubts. With the evolution of ECMAScript, many methods in lodash have been gradually implemented in ES6+. So is it still necessary to learn it? In fact, there are still many very useful tool functions in lodash. For example, anti-shake throttling is often used in front-end work. Don’t you want to write a function by hand every time, right? What's more, I can't write it without a little JS skills.

Let’s take a look at two methods of arrays: slice and splice.

  • slice returns the specified part in the array, and does not change the original array
  • splice: Returning the array to the array will change the original array
let array = [1, 2, 3, 4, 5]
// Pure functions((0, 3))
((0, 3))
((0, 3))
// Impurity function((0, 3))
((0, 3))
((0, 3))

Benefits of pure functions

Cacheable

Because pure functions always have the same result for the same input, the results of pure functions can be cached.

function getArea(r) {
    (r)
    return  * r * r
}
function memoize(f) {
    let cache = {}
    return function() {
        let key = (arguments)
        cache[key] = cache[key] || (f, arguments)
        return cache[key]
    }
}
let getAreaWithMemory = memoize(getArea)
(getAreaWithMemory(4))
(getAreaWithMemory(4))
(getAreaWithMemory(4))
// 4
// 50.26548245743669
// 50.26548245743669
// 50.26548245743669

Testable

Pure functions make testing more convenient

Parallel processing

Unexpected situations are likely to occur when operating shared memory data in a multi-threaded environment

Pure functions do not need to access shared memory data, so pure functions can be run arbitrarily in parallel environments (Web Worker)

side effect

// Impuritylet mini = 18
function checkAge (age) {
  return age &gt;= mini
}
// Pure (hard coded, can be solved by currying later)function checkAge (age) {
  let mini = 18
  return age &gt;= mini
}

Side effects make a function impure (as in the above example), pure functions return the same output based on the same input. If the function depends on the external state, the output cannot be guaranteed to be the same, which will bring side effects.

Curry

The concept of currying: When a function has multiple parameters, first pass a part of the parameters to call it (this part of the parameters will never change in the future), then return a new function to receive the remaining parameters and return the result.

Currying can solve the hard-coded problem in the above code

// Ordinary pure functionsfunction checkAge(min, age) {
    return age &gt;= min
}
// Currying of functionsfunction checkAge(min) {
    return function(age) {
        return age &gt;= min
    }
}
// Of course, the above code can also be modified with the arrow function in ES6const checkAge = (min) =&gt; (age =&gt; age &gt;= min)

Let's write a curry function by hand

function curry(func) {
  return function curriedFn(...args) {
    if ( < ) {
      return function() {
        return curriedFn(...((arguments)))
      }
    }
    return func(...args)
  }
}

Function combination

After reading so much code, you will definitely think that there are many return in the function that doesn't look very good, and the fact is true, so this requires the concept of function combination.

Pure functions and curry can easily write onion code h(g(f(x))))

Get the last element of the array and convert it into capital letters, .toUpper(.first(_.reverse(array))) (These are methods in lodash)

Function combination allows us to recombine fine-grained functions to generate a new function

You can think of it as a pipeline. You split the fn pipeline into three pipelines: fn1, fn2, and fn3, that is, encapsulate different processing logics in different functions, and then integrate them through a compose function to turn it into a function.

fn = compose(f1, f2, f3)
b = fn(a)

Functor (Functor)

What is Functor

  • Container: Contains the deformation relationship between values ​​and values ​​(this deformation relationship is a function)
  • Functor: It is a special container, implemented by an ordinary object, which has a map method, and a function in the map line processes the value (deformed relationship)
// Functor functor A container, wrapping a valueclass Container {
    constructor(value) {
        this._value = value
    }
    // map method, pass in deformation relationship, map each value in the container to another container    map(fn) {
        return new Container(fn(this._value))
    }
}
let r = new Container(5)
    .map(x =&gt; x + 1)
    .map(x =&gt; x * x)

(r)  // 36

Summarize

  • Functional programming operations do not operate values ​​directly, but are completed by functors.
  • Functor is an object that implements map contract
  • We can think of functors as a box, which encapsulates a value
  • To process the value in the box, we need to pass a function (pure function) to the box's map method, and this function will process the value.
  • The final map method returns a box (functor) containing the new value

Maybe you are not used to seeing the new keyword in the code, so you can implement a of method in the container.

class Container {
    static of (value) {
        return new Container(value)
    }
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return (fn(this._value))
    }
}

MayBe functor

If a null or undefined is passed in the above code, the code will throw an error, so another method needs to be implemented.

class MayBe {
    static of(value) {
        return new MayBe(value)
    }
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return () ? (null) : (fn(this._value))
    }
    isNothing() {
        return this._value == null // Here the double equal sign is equivalent to this._value === null || this._value === undefined    }
}

If you look at the above code, is it better to be more robust?

Either functor

In MayBe functors, it is difficult to confirm which step produces the null value problem. So there is Either

class Left {
    static of(value) {
        return new Left(value)
    }
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return this
    }
}
class Right {
    static of(value) {
        return new Right(value)
    }
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return (fn(this._value))
    }
}
function parseJSON(str) {
    try {
        return ((str))
    } catch (e) {
        return ({ error:  })
    }
}

This is the article about the introduction to JavaScript functional programming implementation. For more related JS functional programming content, please search for my previous articles or continue browsing the related articles below. I hope you will support me in the future!