This is the seventh day of my participation in the August More text Challenge. For details, see:August is more challenging

Function Programming (FP), FP is one of the Programming paradigms, we often hear Programming paradigms such as object-oriented procedural Programming, object-oriented Programming.

  • A way of thinking about object-oriented programming

    Things in the real world are abstracted into classes and objects in the program world, and the relationship between things and events is demonstrated through encapsulation, inheritance and polymorphism

  • Functional programming thinking

    To abstract things and relationships from the real world into the programming world (to abstract an operational process)

    • The essence of the program: according to the input through some operation to get the corresponding output, the process of program development will involve a lot of input and output functions
    • The function in functional programming refers not to the function (method) in the program, but to the function in mathematics that is the mapping relationship, for example: y=sine(x), the relationship between x and y
    • The same input always gives the same output (pure function)
    • Functional programming describes mapping between data (functions)

Advantages:

  • You can reuse code
  • In the process of functional expression, the functions we abstract are fine-grained functions that we can combine to make more powerful functions
// Non-functional
let num1 = 2
let num2 = 3
let sum = num1 + num2
console.log(sum)
/ / function type
function add(num1,num2){
    return num1 + num2
}
let sum = add(2.3)
console.log(sum)
Copy the code

Functions are First class citizens

  • Functions can be stored in variables

    // Assign the function as a variable
    // Function expression
    let fn = function () {
        console.log("Hello function first-class citizen")
    }
    fn()
    
    // An example
    
    const BlogController = {
        /* If a function wraps around another function and is of the same form, we can think of it as a function that assigns the value of views. index to index, and a function that assigns the value of index to the method itself, not the return value
        index (posts) { return Views.index(posts) },
        show (posts) { return Views.show(posts) },
        create (posts) { return Views.create(posts) },
        update (posts) { return Views.update(posts) },
        destroy (posts) { return Views.destroy(posts) },
    }
    
    / / optimization
    Assign a method to another method or function
      const BlogController = {
        index : Views.index,
        show : Views.show,
        create : Views.create,
        update : Views.update,
        destroy : Views.destroy,
    }
    Copy the code
  • Functions can be taken as arguments

  • Functions can be returned as values

In javaScript, a function is just a normal object (you can use new Funciton ()), we can store the function in a variable/array, and it can be used as an argument and return value to another function. We can even construct a new Function with new Function(‘alert(1)’) while the program is running

Hight-order Function

  • You can pass a function as an argument to another function as an argument to make the function more flexible and call foreach without considering the internal implementation
// Foreach iterates through each element in the array and then processes each element

function forEach(array, fn) {
    for (let i = 0; i < array.length; i++) {
        fn(array[i])
    }
}

/ / test
let arr =[1.3.5.4.9]
forEach(arr,function(item){
    console.log(item)
})

//filter The elements in the array that meet the condition
function filter(array, fn) {
    let results = []
    for (let i = 0; i < array.length; i++) {
        if (fn(array[i])) {
            results.push(array[i])
        }
    }
    return results
}
/ / test
let arr = [1.3.5.4.9]
let r = filter(arr, function (item) {
    return item % 2= = =0
})
console.log(r)
Copy the code
  • You can treat a function as the return result of another function
function makeFn() {
    let msg = 'make funciton'
    return function () {
        console.log(msg)
    }
}
// The function to receive the return from makeFn
let fn = makeFn()
fn()

// The first () represents the makeFn and the second () represents the function returned by the makeFn
makeFn()()

//once
// The function executes only once, no matter how many times the user clicks
function once(fn) {
    //done to record whether the FN was executed
    let done = false
    return function () {
        // Fn has not been executed yet
        if(! done) { done =true
            // Fn is called using apply. Arguments are used to call the current function
            // The use of apply is executed immediately, which is different from bind
            return fn.apply(this.arguments)}}}let pay = once(function(money){
	console.log(` pay:${money}RMB`)
})

pay(5) // Payment: 5RMB
pay(5)
pay(5)
pay(5)
Copy the code

Meaning of using higher-order functions

  • Abstractions can help us block out the details and focus only on our goals
  • Higher-order functions are used to abstract general purpose problems
  • Make the code more concise
  • You can make your code more flexible
// Process-oriented approach
// Pay attention to details
let array =[1.2.4.6.7]
for(let i=0; i<array.length; i++){console.log(array[i])
}

// Higher order function
let array =[1.2.4.6.7]
// Control of loop variables is not required
forEach( array,item= > {
    console.log(item)
}

// Filter the elements of the array. The conditions of the filter are determined by the conditions passed in. There is no need to pay attention to the internal implementation details, making the code more concise
let r = filter(array,item= > {
    return item % 2= = =0
})
Copy the code

Commonly used higher order functions

  • The forEach, filter,
// Higher order function - takes the function as an argument
// Foreach iterates through each element in the array and then processes each element
function forEach(array, fn) {
    for (let i = 0; i < array.length; i++) {
        fn(array[i])
    }
}

/ / test
let arr =[1.3.5.4.9]
forEach(arr,function(item){
    console.log(item)
})

//filter The elements in the array that meet the condition
function filter(array, fn) {
    let results = []
    for (let i = 0; i < array.length; i++) {
        if (fn(array[i])) {
            results.push(array[i])
        }
    }
    return results
}
/ / test
let arr = [1.3.5.4.9]
let r = filter(arr, function (item) {
    return item % 2= = =0
})
console.log(r)
Copy the code
  • Map, every, some
//map array some

/*map iterates over each element in the array, processing each element, and returning the result to a new array */
const map = (array, fn) = > {
    const results = []
    // For of is an abstraction of for
    for (let value of array) {
        results.push(fn(value))
    }
    return results
}

/* Summary: The argument to the map function is a function, which is a higher-order function. You can specify a function to evaluate any element in the array. Function arguments make our map function more flexible */
let arr = [1.5.7.9.12]
arr = map(arr, v= > v * v)
console.log(arr)

/*every is used to determine whether the elements in the array match a condition that we specify
const every = (array, fn) = > {
    let result = true
    for (let value of array) {
        result = fn(value)
        if(! result) {break}}return result
}

/ / test
let arr = [1.5.7.9.12]
let result = every(arr, v= > v > 0)
console.log(result)

/* some is similar to every to check if there is one element in our array that satisfies our condition */
const some = (array, fn) = > {
    let result = false
    for (let value of array) {
        result = fn(value)
        if (result) {
            break}}return result
}

/ / test
let arr = [1.5.7.9.11]
let r = some(arr, v= > v % 2= = =0)
console.log(r)
Copy the code

Array Basics

Create an array

Creating an Array using literal notation does not call the Array constructor’s new static methods for creating arrays: from () and of ()

  • From is used to convert an array-like structure to an array instance
  • Of is used to convert a set of parameters into an array instance
/* array.from () is used to convert an array-like structure to an Array instance */
console.log(Array.from('12345'));

// Make a shallow copy of the array
const a1 = [1.4.5.7]
const a2 = Array.from(a1)
console.log(a2)/ /,4,5,7 [1]
console.log(a1 == a2)//false

// Task iterables can be used

// Arguments can easily be converted into an array

function getArgsArray() {
    return Array.from(arguments)}console.log(getArgsArray(1.3.4.5))//[1, 3, 4, 5]

/ / of can be used to realize, is used to replace the commonly used before es6 Array. The prototype. Slice. The call (the arguments) this will be written into the Array parameters
console.log(Array.of(1.3.4.5))//[1, 3, 4, 5]

// From can also transform a custom object with the necessary properties
const arrayLikeObject = {
    0: 1.1: 5.2: 7.1: 0.'length': 6
}
console.log(Array.from(arrayLikeObject))
/*[ 1, 0, 7, undefined, undefined, undefined ]*/

//from() can also take an optional second argument to the mapping function, which enhances the value of the new array, and an optional third argument to specify this in the mapping function, but this overridden value of this is not appropriate in the arrow function
const b1 = [1.2.4.5]
const b2 = Array.from(b1, x= > x ** 2)
const b3 = Array.from(b1, function (x) {
    return x ** this.exponent
}, {
    exponent: 2
})

console.log(b2)//[1, 4, 16, 25]

console.log(b3)//[1, 4, 16, 25]
Copy the code

An array of space

Es6 is a little different from the previous version. The new methods in ES6 generally treat these gaps as if they were existing elements, but with undefined values

const options = [1.5]
for(const i of options){
    console.log(option === undefined)}/*'
false
true
true
true
true
false
*/

Copy the code

Methods prior to ES6 ignore this gap, but methods vary from method to method

let options = [1.5]
// Map will skip this space
console.log(options.map((item) = >6))/ / [6,,,, 6]
//join treats the null position as an empty string
console.log(options.join(The '-'))1-5 "/ /"
Copy the code

Because of the inconsistencies and performance implications, array vacancies should be avoided in practice and, if needed, used undefined instead

Array index

The length attribute is not read-only and can be modified to remove or add elements from the end of the array

let colors = ['red'.'blue'.'green']
colors.length = 2
console.log(colors[2])//undefined
Copy the code

An array can contain up to 4 294 967 295 elements about 4.3 billion

Array detection

Using the instanceof operator is sufficient to detect whether an object is an array with only one global scope,

You can use array.isarray () when you are not sure in which global context to create

Array iterator method

The Array Array prototype exposes three methods for retrieving the contents of an Array

  • keys()
  • values()
  • entries()
let arr1 = ['foo'.'bar'.'baz'.'qux']
/* These methods all return iterators that can be converted directly to an array instance by arrayf.from () */
console.log(Array.from(arr1.keys()))/ / [0, 1, 2, 3]
console.log(Array.from(arr1.values()))//['foo','bar','baz','qux']
console.log(Array.from(arr1.entries()))//[[0,'foo'],[1,'bar'],[2,'baz'],[3,'qux']]
Copy the code

closure

You can call an inner function of a function in another scope and access members of that function’s scope

Core action: extends the scope of the internal variables of the external function

// Function as return value
function makefn(){
    let msg = `hello funciton`
    return function (){
        console.log(msg)
    }
}
/ * * /
const fn = makefn()
fn()


/*
once
*/
function once(fn){
    let done =false
    return funciton(){
        if(! done){ done=true
            return fn.apply(this.arguments)}}}let pay = once(funciton (money)) {
    console.log(` pay:${money}RMB`)}

// Only one payment will be made
pay(5)
pay(5)
pay(5)
pay(5)
Copy the code

The nature of closures: the function is placed on an execution stack when it is executed, and is removed from the stack when it is finished, but the scope members on the heap cannot be freed by external references, so the inner function can still access the members of the outer function

Pure functions

The same input always yields the same output, without any observable side effects

  • A pure function is a mathematical function that describes the relationship between inputs and outputs, y=f(x).
  • Lodash is a pure function library that provides methods for manipulating arrays, numbers, objects, strings, functions, and more
  • Slice and splice are pure and impure functions, respectively
    • Slice returns the specified portion of the array without changing the original array
    • Splice performs an operation on an array and returns that array, changing the array (delete, modify)
// Pure and impure functions

let array = [1.2.5.8.0]

// A pure function means that the same input always has the same output
console.log(array.slice(0.3))
console.log(array.slice(0.3))
console.log(array.slice(0.3))
/ /,2,5 [1]

// Splice is an impure function
console.log(array.splice(0.3))
console.log(array.splice(0.3))
console.log(array.splice(0.3))
/ /,2,5 [1] [8, 0] []
Copy the code
  • Functional programming does not preserve the intermediate results of calculation, so variables are immutable (stateless)
  • We can hand over the results of one function to another

Loadsh Pure function library

/ / demo lodash
// first last toUpper reverse each 
// The new es6 property includes find findIndex
const _ = require('lodash')
const array = ['jack'.'tom'.'lucy'.'kate']
console.log(_.first(array))
console.log(_.last(array))

console.log(_.toUpper(_.first(array)))

// Reverse has no arguments and is not a pure function
console.log(array.reverse(), 'array.reverse')

console.log(_.reverse(array))

// Each is the alias of forEach
const r = _.each(array, (item, index) = > {
    console.log(item, index)
})
console.log(r)

console.log(array.includes(1), 'array.includes(1)')
console.log(array.find(item= > item.length > 3), 'array.find(1)')
console.log(array.findIndex(item= > item.length > 3), 'array.findIndex(1)')
Copy the code

ECMAScript provides two types of methods for searching arrays: strict equality search and assertion function search

Strictly equal

  • indexOf()
  • lastIndexOf()
  • Includes () is new in ES7

Each of these methods takes two arguments: the element to look for and an optional starting position for the search, with the second argument of lastIndexOf representing the position of the last element

The indexOf and Incudes methods search backwards. LastIndexOf searches forward from the end of the array, but lastIndexOf returns the same position as indexOf. Both indexOf and indexLastOf return the position of the element in the array. -1 includes returns a Boolean value indicating whether an item matching the specified element was found

When comparing the first argument to each item in the array, the === comparison is used, which means that the two items must be strictly equal

let num = [1.2.3.4.5.4.3.2.1.6]

console.log(num.indexOf(4))/ / 3
console.log(num.lastIndexOf(6))/ / 9
console.log(num.includes(4))//true

console.log(num.indexOf(3.2))//true
console.log(num.lastIndexOf(3.4))//true

let person = { name: 'nike' }
let people = [{ name: 'nike' }]
let morePeople = [person]

console.log(people.indexOf(person))
console.log(morePeople.indexOf(person))
console.log(people.includes(person))
console.log(morePeople.includes(person))
Copy the code

Assertion functions

The assertion function takes three arguments: Element, index, and array itself Find () and fineIndex() both start with the minimum index of the array, find returns the first matching element, and findIndex returns the index of the first matching element. Both take an optional second argument that specifies the value of the internal this. Once a match is found, Neither of these approaches continues to be considered.

// Predicate function
const people = [
    {
        name: 'Matt'.age: 28
    }, {
        name: 'nike'.age: 30}]// Find returns the first matched element, and findIndex returns the index of the first matched element
console.log(people.find(item= > item.age > 28))
console.log(people.findIndex(item= > item.age > 28))

// After a match is found, both methods do not continue the search.
let nums = [3.6.9]
nums.find((item, index, array) = > {
    console.log(item)
    console.log(index)
    console.log(array)
    return item > 1
})
Copy the code

The benefits of pure functions

  • cacheable
    • Because pure functions always have the same result for the same input, the result of a pure function can be cached primarily to improve performance
// Memory function

const _ = require('lodash')
function getArea(r) {
    console.log(r)
    return Math.PI * r * r
}
// Returns a function with memory
// let getAreaWithMemory = _.memoize(getArea)
// console.log(getAreaWithMemory(4))
// console.log(getAreaWithMemory(4))
// console.log(getAreaWithMemory(4))
// console.log(getAreaWithMemory(4))

// Simulate memoize implementation

function memoize(fn) {
    let cache = {}
    return function () {
        let key = JSON.stringify(arguments)
        cache[key] = cache[key] || fn.apply(fn, arguments)
        return cache[key]
    }
}

let getAreaWithMemory = memoize(getArea)
console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))
Copy the code
  • Testable pure functions make testing easier
  • Parallel processing
    • Working in parallel with shared memory data in a multi-threaded environment is likely to lead to unexpected situations
    • Pure functions do not need to access shared memory data, so it is possible to run pure functions in a parallel environment (web Worker added after ES6, can open multiple threads).

Side effects

Pure functions: Always get the same output for the same input, without any observable side effects

// Impure function
// Functions that depend on external states cannot guarantee the same output
let mini = 18 // It brings side effects
function checkAge () {
    return age >= mini
}

// Pure function (hardcoded, can be later curlized to solve)
function checkAge () {
    let mini = 18// is a specific number, hard coded, should be avoided as much as possible
    return age > = mini
}
Copy the code

The side effect is to make a function impure (as shown above). A pure function returns the same output for the same input, which can be a side effect if the function is dependent on an external state and cannot guarantee the same output.

Source of side effects:

  • The global variable
  • The configuration file
  • The database
  • Gets the user’s input
  • .

All external interactions are likely to produce side effects and side effects which resulted in the reduction method is universal is not suitable for extension and reusability, and side effects will bring potential safety hazard for applications (for example, when we get the user input may lead to cross-site scripting attacks), bring uncertainty to the program, but the side effects could not be completely banned, Keep them under control as much as possible.

(Haskell Brooks Curry)

// Curryization of the function
function checkAgeB(min) {
    return function (age) {
        return age >= min
    }
}
//es6 arrow function
let checkAgeB = min= > (age= > age >= min)

let checkAgeB18 = checkAgeB(18)
let checkAgeB20 = checkAgeB(20)

console.log(checkAgeB18(25))
console.log(checkAgeB18(20))
console.log(checkAgeB20(20))
console.log(checkAgeB20(24))
Copy the code

Corey:

  • When a function has multiple arguments it is called by passing some of the arguments first (these arguments never change)
  • It then returns a new function that takes the remaining arguments and returns the result

Currying in Lodash

// Curry is basically used in lodash
const _ = require('lodash')

// Colitisation can help us transform any function of several variables into a function of one
function getSum(a, b, c) {
    return a + b + c
}

const curried = _.curry(getSum)
GetSum is called immediately and returns the result if all the required parameters are passed in
console.log(curried(1.2.3))
// If the arguments passed are partial arguments, it returns a function and waits to receive other arguments
console.log(curried(1) (2.3))
console.log(curried(1) (2) (3))
Copy the code

The Currie case

// The Caulification Case
const _ = require('lodash')
// Matches all characters in the string
// ''.match(/\s+/g)
// Matches all numbers in the string
// ''.match(/\d+/g)

// Functional programming maximizes the reuse of functions
// function match(reg,str){
// return str.match(reg)
// }

// Match is curlized
let match = _.curry(function (reg, str) {
  return str.match(reg)
})


const haveSpace = match(/\s+/g)
const haveNum = match(/\d+/g)

console.log(haveSpace('Hello Sine ya'))
console.log(haveNum('Hello2 Sine4 ya1'))

// Find all strings in the array that contain Spaces
const filter = _.curry(function (fn, arr) {
  return arr.filter(fn)
})
const filter = _.curry((fn, arr) = > {
  return arr.filter(fn)
})

const findSpace = filter(haveSpace)

console.log(filter(haveSpace, ['qw e'.'rf p'.'ert']))


console.log(findSpace(['oi p'.'ioj']))
Copy the code

The realization of Curryization

// Simulate the curry method in Lodash
const _ = require('lodash')
function getSum(a,b,c) {
  return a + b + c
}

/* To call _. Curry we pass in a pure function that returns a curlized function */
// const curied = _.curry(getSum)
const curied = curry(getSum)

console.log(curied(1.2.3))
console.log(curied(1.2) (3))
console.log(curied(1) (2) (3))

// We need a curlized function
function curry(fn){
  return function curriedFn(. args){
    // In the first case, we pass in several functions for the currie function
    // Second, the curry function is called with a partial argument and returns a function waiting to receive other arguments
    // We want to get whether the number of parameters is the same as the number of fn arguments
    if(args.length<fn.length){
      return function(){
        // Arguments is a pseudo-array so use array. from to handle arguments
        returncurriedFn(... args.concat(Array.from(arguments)))}}else{
      returnfn(... args) } } }Copy the code

Summary of currie

  • Coreization allows us to pass fewer arguments to a function and get a new function that has some fixed arguments memorized
  • This is a kind of caching of function arguments
  • Make the function more flexible, make the function granularity smaller
  • Functions of multiple variables can be converted to functions of one variable, and functions can be combined to produce powerful functions from the

Function Compose

  • Pure functions and Currying is easy to write the onion code h(g(f(x)))

    • Get the last element of the array and convert it to uppercase _.toupper (.first(.reverse(arr)))
    • Function composition allows us to regroup fine-grained functions into a new function
  • If a function has to go through multiple processes to get its final value, you can combine the intermediate processes into a single function

    • Functions are like conduits of data, and function combination is the process of connecting these conduits, allowing data to flow through multiple conduits to form the final result
    • Function combinations are executed from right to left by default
// Function combination demonstration
function compose(f,g){
    return function(value){
        return f(g(value))
    }
}
/ / flip
function reverse(arr){
    return arr.reverse()
}
// The first element of the array
function first(arr){
    return arr[0]}const last = compose(first,reverse)

console.log(last([2.3.5.6]))
Copy the code

Lodash combination function

-flow () Executes from left to right. -flowright () executes from right to left

// The combination function in lodash _.flowRight
const _ = require('lodash')

const reverse = arr= > arr.reverse()
const first = arr= > arr[0]
const toUpper = s= > s.toUpperCase()

const f = _.flowRight(toUpper,first,reverse)
console.log(f(['we'.'rt4t'.'rer']))
Copy the code

Simulate lodash’s flowRight

// The combination function in lodash _.flowRight
const _ = require('lodash')
const { CodeNode } = require('source-list-map')

const reverse = arr= > arr.reverse()
const first = arr= > arr[0]
const toUpper = s= > s.toUpperCase()

// const f = _.flowRight(toUpper,first,reverse)


// function compose(... args){
// return function(value){
// // performs one of our provided functions for each element in the array and sums it up into a single result
            //acc indicates the last execution result, fn indicates the current pipe
// return args.reverse().reduce(function(acc,fn){
// return fn(acc)
// },value)
/ /}
// }

const compose = (. args) = > value= > args.reverse().reduce((acc, fn) = > fn(acc), value)
const f = compose(toUpper, first, reverse)
console.log(f(['we'.'rt4t'.'rer']))
Copy the code

The combination of functions is associaltivity.

  • We can either combine g with h, or we can combine f with g, and it’s the same thing
let f = compose(f,g,h)
let associaltive = compose(compose(f,g),h) == compose(f,compose(g,h))
//true
Copy the code
// Function combination must satisfy associative law
const _ = require('lodash')
// const f = _.flowRight(_.toUpper,_.first,_.reverse)
// const f = _.flowRight(_.flowRight(_.toUpper,_.first),_.reverse)
const f = _.flowRight(_.toUpper,_.flowRight(_.first,_.reverse))

console.log(f(['we'.'rt4t'.'rer']))
Copy the code

How do I debug composite functions

const f = _.flowRight(_.toUpper,_.first,_.reverse)
// Function combination debugging
//NEVER SAY DIE --> never-say-die

const _ = require('lodash')

// const log=(c)=>{
// console.log(c)
// return c
// }
const trace = _.curry((tag, v) = > {
    console.log(tag, v)
    return v
})

//_.split
const split = _.curry((sep, str) = > _.split(str, sep))
//_.toLower
const map = _.curry((fn, arr) = > _.map(arr, fn))
//_.join
const join = _.curry((seq, arr) = > _.join(arr, seq))

const result = _.flowRight(join(The '-'),trace('Print after map'), map(_.toLower),trace('Print after split'), split(' '))
console.log(result('NEVER SAY DIE'))
Copy the code

FP module in Losash

  • Lodash’s FP module provides a functional programme-friendly approach to use
  • Provides an immutable auto-curried iteratee-first data-last method (which has been currytized to require function preference and data lag if it is a function)
/ / lodash module
// Data first, function lag
_.map(['a'.'b'.'c'],_.toUpper)
//=>["A","B","C"]
_.map(['a'.'b'.'c'])
//['a','b','c']

_.split("Hello World".' ')

/ / lodash/fp module
// Function priority data lag
const fp = require('lodash/fp')
fp.map(fp.toUpper,['a'.'b'.'c'])
fp.map(fp.toUpper)(['a'.'b'.'c'])

fp.split(' '."Hello World")
fp.split(' ') ("Hello World")

// NEVER SAY DIE --> never-say-die
const fp = require('lodash/fp')

let f = fp.flowRight(fp.join(The '-'),fp.map(fp.toLower),fp.split(' '))
console.log(f('NEVER SAY DIE'))
Copy the code

Point Free function coding style

We can define the process of data processing as a data-independent synthesis operation, without the use of the parameter representing the data, as long as the simple operation steps together, before using this pattern we need to define some auxiliary basic operation functions

  • You do not need to specify the data being processed
  • You just have to synthesize it
  • Some auxiliary basic operations need to be defined
// point free
// Hello World => hello_world
const fp = require('lodash/fp')

const pf = fp.flowRight(fp.replace(/\s+/g."_"),fp.toLower)
console.log(pf("Hello World"))
Copy the code