preface
Higher-order functions are functions that operate on other functions, either as arguments or by returning them. Simply put, a higher-order function is a function that takes a function as an argument or returns a function as output.
Examples are array.prototype. map, array.prototype. filter, and array.prototype. reduce.
Welcome to the rest of the JS series
Tail calls and tail recursion
Tail Call is an important concept of functional programming. It is very simple in itself and can be explained in a sentence. The last step in a function is to call another function.
function g(x) {
console.log(x)
}
function f(x) {
return g(x)
}
console.log(f(1))
// In the above code, the last step of function f is to call function g, which is the last call.
Copy the code
In the code above, the last step of function F is to call function G, which is the tail call. A trailing call does not have to occur at the end of a function, just as long as it is the last operation.
The function calls itself, called recursion. If the tail calls itself, it is called tail recursion. Recursion is very memory intensive because you need to hold hundreds or thousands of call frames at the same time, which is prone to stack overflow errors. But queue tailenders say that since there is only one call frame, stack overflow errors never occur.
function factorial(n) {
if (n === 1) {
return 1
}
return n * factorial(n - 1)}Copy the code
The above code is a factorial function, calculate n factorial, need to save n call data at most, the complexity of O (n), if rewritten as a tail call, only retain one call record, the complexity of O (1).
function factor(n, total) {
if (n === 1) {
return total
}
return factor(n - 1, n * total)
}
Copy the code
The Fibonacci sequence can also be used for tail calls.
function Fibonacci(n) {
if (n <= 1) {
return 1
}
return Fibonacci(n - 1) + Fibonacci(n - 2)}/ / tail recursion
function Fibona(n, ac1 = 1, ac2 = 1) {
if (n <= 1) {
return ac2
}
return Fibona(n - 1, ac2, ac1 + ac2)
}
Copy the code
Cauchy function
In mathematics and computer science, Currization is the technique of converting a function that takes multiple arguments into a series of functions that take one argument. Currization is the process of converting a function with more parameters into a function with fewer parameters. For example
// A normal function
function fn(a, b, c, d, e) {
console.log(a, b, c, d, e)
}
// The generated Currization function
let _fn = curry(fn)
_fn(1.2.3.4.5) / / print: 1, 2, 3, 4, 5
_fn(1) (2) (3.4.5) / / print: 1, 2, 3, 4, 5
_fn(1.2) (3.4) (5) / / print: 1, 2, 3, 4, 5
_fn(1) (2) (3) (4) (5) / / print: 1, 2, 3, 4, 5
Copy the code
The realization of chemical function
// Currying the summation function
let f1 = curry(add, 1.2.3)
console.log('Complex Edition', f1()) / / 6
// Currying the summation function
let f2 = curry(add, 1.2)
console.log('Complex Edition', f2(3)) / / 6
// Currying the summation function
let f3 = curry(add)
console.log('Complex Edition', f3(1.2.3)) / / 6
// The complex curry function can be called multiple times, as follows:
console.log('Complex Edition', f3(1) (2) (3)) / / 6
console.log('Complex Edition', f3(1.2) (3)) / / 6
console.log('Complex Edition', f3(1) (2.3)) / / 6
// Complex version (can pass an indefinite number of arguments at a time, only when the total number of arguments passed is not less than the total number of parameters of the function)
function curry(fn) {
/ / closures
// Cache all arguments except fn
let args = Array.prototype.slice.call(arguments.1)
return function() {
// Concatenate cached old arguments with newly passed arguments (i.e., save each passed argument, but do not execute it)
let newArgs = args.concat(Array.from(arguments))
if (newArgs.length < fn.length) {
// The cumulative number of parameters is less than the total number of fn parameters
// Pass fn and accumulated arguments recursively
return curry.call(this, fn, ... newArgs) }else {
/ / call
return fn.apply(this, newArgs)
}
}
}
Copy the code
The use of cremation
Currization actually complicates the simple problem, but it also gives us more freedom in the way we use functions. This freedom to handle function parameters is the core of Currization. The essence of currification is to reduce generality and improve applicability. Here’s an example:
At this time, we will encapsulate a universal function checkByRegExp to receive two parameters, the checked regular object and the string to be checked
function checkByRegExp(regExp, string) {
return regExp.text(string)
}
checkByRegExp(/^1\d{10}$/.'18642838455') // Verify the phone number
checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/.'[email protected]') // Verify the mailbox
Copy the code
We need to input a string of regees each time to perform verification, and we need to write the same regees multiple times to verify the same type of data, which leads to low efficiency in use, and the function checkByRegExp itself is a tool function and has no significance. At this point, we can use the kerrization to encapsulate the checkByRegExp function to simplify code writing and improve code readability.
// Perform currization
let _check = curry(checkByRegExp)
// Generate utility functions to verify phone numbers
let checkCellPhone = _check(/^1\d{10}$/)
// Generate utility functions to validate the mailbox
let checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/)
checkCellPhone('18642838455') // Verify the phone number
checkCellPhone('13109840560') // Verify the phone number
checkCellPhone('13204061212') // Verify the phone number
checkEmail('[email protected]') // Verify the mailbox
checkEmail('[email protected]') // Verify the mailbox
checkEmail('[email protected]') // Verify the mailbox
Copy the code
Currization of the function argument length
Some implementations of currying use fn.length to express the number of arguments of a function, that is, the number of arguments of a function. And it isn’t.
The length attribute of the function takes the number of parameters, but the number of parameters does not include the number of remaining parameters, and only includes the number of parameters before the first one has a default value, as shown in the following example.
((a, b, c) = > {}).length
/ / 3
((a, b, c = 3) = > {}).length
/ / 2
((a, b = 2, c) = > {}).length
/ / 1
((a = 1, b, c) = > {}).length
/ / 0
((. args) = > {}).length
/ / 0
const fn = (. args) = > {
console.log(args.length)
}
fn(1.2.3)
/ / 3
Copy the code
Compose function
Compose is a composite function that executes sub-functions in series. The output of one function is the input parameter of another function. Once the first function is executed, the subsequent functions are derived and executed like dominoes.
const greeting = name= > `Hello ${name}`
const toUpper = str= > str.toUpperCase()
toUpper(greeting('Onion')) // HELLO ONION
Copy the code
Characteristics of the compose function
- Compose takes a function as an argument, executes from right to left, and returns a type function
- All arguments of fn() are passed to the rightmost function, and the result is passed to the penultimate function
The realization of the composer
var compose = function(. args) {
var len = args.length // The number of args functions
var count = len - 1
var result
return function func(. args1) {
// Enumeration of args1 arguments to the func function
result = args[count].call(this, args1)
if (count > 0) {
count--
return func.call(null, result) // result Returns the result of the previous function
} else {
// restore the initial state of count
count = len - 1
return result
}
}
}
Copy the code
For example
var greeting = (name) = > `Hello ${name}`
var toUpper = str= > str.toUpperCase()
var fn = compose(toUpper, greeting)
console.log(fn('jack'))
Copy the code
The familiar loader execution sequence in Webpack is from right to left, because WebPack selects the compose mode. The loader execution sequence is from right to left, and each loader is a function.
rules: [
{ test: /\.css$/.use: ['style-loader'.'css-loader']}]Copy the code
As shown above, Webpack uses style-loader and CSS-loader, which first loads. CSS files with csS-loader, and then style-loader injects internal styles into our HTML pages.
The compose code in webpack looks like this:
const compose = (. fns) = > {
return fns.reduce(
(prevFn, nextFn) = > {
return value= >prevFn(nextFn(value))
},
value => value
)
}
Copy the code