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

JS Basic summary (1) – data types
JS Basic Summary (2) – prototype and prototype chain
JS Basics (3) – scope and closure
This refers to call/apply/bind
EventLoopd is the implementation mechanism for JAVASCRIPT

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

Recommend the article

Summarize the way javascript handles asynchrony
Summary of mobile H5 development tips.
Build a WebPack project from scratch
Summary of several webpack optimization methods
Summarize the methods of front-end performance optimization
Summary of advanced application of VUE knowledge system
Summarize the practical skills of vUE knowledge system
Several common JS recursive algorithms
Encapsulate a TOAST and Dialog component and publish it to NPM
This article covers front-end routing, back-end routing, single-page application, multi-page application
Discussion on JavaScript anti – shake and throttling