preface

In JavaScript, functions can not only be called, but can also be assigned, passed, and returned just like normal variables, so we say that JavaScript functions are first-class citizens of the JavaScript language. A Function is called a High Order Function if it can be passed in as an argument to another Function, or if it returns to a Function.

Higher-order functions in JavaScript

JavaScript defines many higher-order functions that let us, for example:

  • Array.prototype.map()
  • Array.prototype.reduce()
  • Array.prototype.every()
  • Array.prototype.some()
  • Array.prototype.filter()
  • .

Each of these functions can take a function as an argument.

// Compute the array sum
function countSum(a, b) {
    return a + b;
}
const arr = [1.2.3.4.5];
const sum = arr.reduce(countSum);
console.log(sum);  / / 15
Copy the code

Array.prototype.sort(); array.prototype.sort (); array.prototype.sort (); array.prototype.sort (); array.prototype.sort ();

const arr = [5, -1.3, -6.7, -1];
arr.sort(); 
console.log(arr);  //[-1, -1, -6, 3, 5, 7]
Copy the code

The sorting result is neither ascending nor descending. This is because when sort() is used, the default sort order is to compare elements’ UTF-16 code unit value sequences after converting them to strings. So we’re actually comparing utF-16 code unit values after the Number type is converted to a string.

Sort () also supports passing in a function that supports certain types of sorting. This function returns a value similar to that of JAVA’s java.lang.Comparable interface:

// Ascending sort
const arr = [5, -1.3, -6.7, -1];
arr.sort((a, b) = > a - b);
console.log(arr);  //[-6, -1, -1, 3, 5, 7]

// Sort in descending order
const arr = [5, -1.3, -6.7, -1];
arr.sort((a, b) = > b - a);
console.log(arr);  //[7, 5, 3, -1, -1, -6]

// Multi-dimensional array sort
const arr = [[1.2], [...1.1], [1.0], [...1.0]];
arr.sort((a, b) = > a[0] === b[0]? a[1] - b[1] : a[0] - b[0]);
console.log(arr);  //[[-1, 0], [-1, 1], [1, 0], [1, 2]
Copy the code

Fundamentals of higher order functions

Having said that, how do we define a higher-order function? Again, to review the concept of higher-order functions: a function is called higher-order if it can be passed in as an argument to another function, or if it returns to another function.

A simple forEach function

Let’s consider a scenario where the array.prototype.foreach () method is not supported in a browser…

If we want to consider compatibility with this browser, we can try polyfill this function. A common polyfill template is as follows:

(function () {
if (!Array.prototype.forEach) {
  Array.prototype.forEach = function (func) {
    // Implement it here}}}) ()Copy the code

The forEach function takes as an argument a function that takes the current traversal value, the array index currently traversed, and the current array. Of course, forEach also supports passing a second parameter as a context for passing in functions, and since we are implementing a simple forEach, we ignore the second parameter. A more complete polyfil implementation of forEach is available later.

To fill in the details above, first the parameter func cannot be empty and it is a function:

if(typeoffunc ! = ='function') {throw new TypeError(func.toString() + `is not a function`)}
Copy the code

Iterate through the array and pass the corresponding arguments to func, noting that the forEach function returns no value.

const arr = this;
const length = arr.length >>> 0;   // Unsigned move right 0 bits, ensure length is a positive integer
let k = 0;
while(k < length) {
  if(k in arr) {
    func(arr[k], k, arr);
  }
  k++;
}
Copy the code

Thus, a simple forEach function is complete.

(function () {
if (!Array.prototype.forEach) {
  Array.prototype.forEach = function (func) {
    if (typeoffunc ! = ='function') {
      throw new TypeError(func.toString() + `is not a function`)}const arr = this;
    const length = arr.length >>> 0;   // Unsigned move right 0 bits, ensure length is a positive integer
    let k = 0;
    while (k < length) {
      if (k inarr) { func(arr[k], k, arr); } k++; }}}}) ()Copy the code

To sum up, we implement a function that takes a parameter, and we don’t care about the content or implementation of the parameter. We just call the function in forEach, so that the function “takes effect”.

HOF0

Here’s another example:

function HOF0(func) {
  const resFunc =  function (. args) {
    return func.apply(null, args);
  }
  return resFunc;
}
Copy the code

This can be called a normal form of higher-order functions, where HOF0 passes in a function func, and HOF0 returns an anonymous function, which returns a call to the func function. Regardless of context, the following two calls are the same:

function sum(a, b) {
  return a + b;
}
let countSum = HOF0(sum);   // Returns a function
countSum(1.2) === sum(1.2);
Copy the code

So why complicate it? Don’t worry, we will soon apply this HOF0 function.

Higher order functions advance

In the interview process, the interviewer will often ask us to write throttling, anti-shaking functions, these functions are high order functions. Once you’ve mastered the above, writing these functions by hand is a breeze!

Handwriting function throttling

Function throttling: Ensures that event handlers are used once in a specified period of time when events are continuously emitted.

First of all, to ensure that the execution of a period of time only once, so you can use a timer to control the “times”, and then in this period of time to trigger an event handler function, this is not to call an event handler function in the function! So let’s take the above HOF0 function and modify it:

//delay indicates the time to be delayed
function throttle(func, delay) {
  const resFunc =  function (. args) {
    func.apply(null, args);
  }
  return resFunc;
}
Copy the code

Apply (null, args) to setTimeout. Delay is used as the delay

//delay indicates the time to be delayed
function throttle(func, delay) {
  const resFunc =  function (. args) {
    setTimeout(() = > {
      func.apply(null, args);
    }, delay)
  }
  return resFunc;
}
Copy the code

We try to execute this function and find that it executes every time it is called, with no “interval” implemented. This is because resFunc calls generate a new timer each time, so we also need to prevent the function from generating a new timer for the duration of the delay, where we use closures.

//delay indicates the time to be delayed
function throttle(func, delay) {
  let timer = null;
  const resFunc =  function (. args) {
    if(timer == null) {
      timer = setTimeout(() = > {
        func.apply(null, args);
        timer = null;
      }, delay)
    }
  }
  return resFunc;
}
Copy the code

Now we have a higher-order function! Example code: codepen. IO /hengistchan…

If you’re wondering why you’re returning a function that references throttle’s timer variable, check out closures.

Handwriting function anti shake

Debounce: When an event is continuously triggered and no event has been triggered for a certain period of time, the event handler is executed once. If the event is triggered again before the specified time, the delay is restarted.

Following the above analysis, you only need to change the throttling code. If the event is triggered, the timer is cleared and a new timer is created.

//delay indicates the time to be delayed
function debounce(func, delay) {
  let timer = null;
  const resFunc =  function (. args) {
    if(timer ! =null) clearTimeout(timer);
    timer = setTimeout(() = > {
      func.apply(null, args);
    }, delay)
  }
  return resFunc;
}
Copy the code

Example code: codepen. IO /hengistchan…

conclusion

A Function is called a High Order Function if it can be passed in as an argument to another Function, or if it returns to a Function. In addition to function stabilization and function throttling, higher-order functions have many applications, such as limiting the number of times a function is executed, and currizing functions. After waiting for me to find practice, fill again slowly 😭.

Add: a complete polyfill implementation of forEach, from which more polyfills can be added or removed, such as Map, reduce, some, etc.

Array.prototype.forEach = function (func, thisArg = window) {
  if (this= =null) throw new Error("");

  if (typeoffunc ! = ='function') throw new Error("is not a function");

  // Why use Object(this) here? For reference:
  //https://stackoverflow.com/questions/66941001/in-the-array-prototype-finds-implementation-why-do-we-need-to-use-objetct this
  const O = Object(this);
  const length = O.length >>> 0;
  let k = 0;
  while(k < length) {
    if (k inO) { func.call(thisArg, O[k], k, O); } k++; }};Copy the code

This article is part of the “Gold Nuggets For Free!” Event, click to view details of the event