Higher-order functions in JavaScript

foreword

In the process of learning JavaScript, we may have more or less exposure to higher-order functions. So, do we have a clear definition for this ourselves, or are we very proficient in mastering these usages?

If there are any flaws or mistakes in the article, please give me more advice from the friends who see it, thank you in advance

Below↓

In simple terms, a higher-order function is a function that receives a function as an argument or returns a function as an output

When you see such a concept, what functions will appear in your mind?

In fact, some array methods that we often use, such as: map, filter, etc. are all categories of higher-order functions

Of course, these built-in methods in JavaScript are not within the scope of this article. Here are some high-level usages of functions that we may encounter during actual development or interviews.

 

anti-shake

When tasks are triggered frequently, the task will be executed only when the interval between task triggers exceeds the specified interval.

The implementation method is that if the frequency of task triggering is less than the time we set, then we will clear the previous task and re-time it.

function debounce(fn, interval) {
    let timer = null
    return function() {
        // If the user triggers again within the set time, it will be cleared
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, interval);
    }
}

 

throttling

The task will only be executed once within the specified time interval
function throttle(fn, interval) {
    let timer,
        firstTime = true // Is it the first execution
    return function () {
        let _this = this
        if (firstTime) {
            fn.apply(_this, arguments) // No need to delay execution the first time
            return firstTime = false
        }
        if (timer) { // If the timer is still, it means that the previous one has not been executed.
            return false
        }
        timer = setTimeout(() => {
            clearTimeout(timer)
            timer = null
            fn.apply(_this, arguments)
        }, interval || 500);
    }
}

// Add a throttle directly without considering the timer
function throttle(fn, interval) {
    let canRun = true //throttle valve
    return function() {
        if(!canRun) {
            return
        }
        canRun = false
        setTimeout(() => {
            fn.apply(this, arguments)
            canRun = true
        }, interval);
    }
}

Whether it is anti-shake or throttling, we can use the following method to verify

window.onresize = throttle(function () {
    console.log(1)
}, 1000)

Through the implementation of the above two methods, I believe that my friends also understand that the so-called anti-shake and throttling are achieved by means of setTimeOut, the difference is the position of clearing the timer

 

lazy function

When we need to reuse a logic, optimize logic judgment and improve JavaScript performance

Principle: Override the function of the same name

function createXHR() {
    var xhr
    try {
        xhr = new XMLHttpRequest()
    } catch (e) {
        handleErr(e)
        try {
            xhr = new ActiveXObject('Msxml2.XMLHTTP')
        } catch (e) {
            try {
                xhr = new ActiveXObject('Microsoft.XMLHTTP')
            } catch (e) {
                xhr = null
            }
        }
    }
    return xhr
}

function handleErr(e) {
    // do sth
}

Modified version of lazy function:

function createXHR() {
    var xhr
    if(typeof XMLHttpRequest !== 'undefined') {
        xhr = new XMLHttpRequest()
        createXHR = function() {
            return new XMLHttpRequest()
        }
    } else {
        try {
            xhr = new ActiveXObject('Msxml2.XMLHTTP')
            createXHR = function() {
                return new ActiveXObject('Msxml2.XMLHTTP')
            }
        } catch(e) {
            try {
                xhr = new ActiveXObject('Microsoft.XMLHTTP')
                createXHR = function() {
                    return new ActiveXObject('Microsoft.XMLHTTP')
                }
            } catch(e) {
                createXHR = function() {
                    return null
                }
            }
        }
    }
    return xhr
}

After the above modification, when we call this function for the first time, we will judge the current environment, and then optimize the abbreviation of the function, and do not need to make subsequent judgments.

For example, if the XMLHTTPRequest in the above code exists, then when we call this function for the second time, it is already like this

function createXHR() {
    return new XMLHttpRequest()
}

scenes to be used:

  • Frequent use of the same judgment logic
  • Only need to judge once, the subsequent use environment is stable

 

cascade function

In fact, it is a chain call, so the principle is very simple: return the object itself in each method

Suppose we have a Person template

function Person() {}
// add a few methods
Person.prototype = {
    setName: function (name) {
        this.name = name
        return this //
    },
    setAge: function (age) {
        this.age = age
        return this
    },
    setSex: function (sex) {
        this.sex = sex
    },
}
// Don't forget to re-specify the constructor
Person.constructor = Person
let person = new Person()
// This seems to do a lot of repetitive things. With a little modification, the effect of chaining calls can be achieved by returning this in each method.
person.setName('wandering de tadpole')
person.setAge(18)
person.setSex('male')
// after modification
person.setName('wandering de tadpole').setAge(18).setSex('male')
console.log(person)

Advantage: Simplifies the steps of function calls, we don't need to write some repetitive things

Disadvantage: occupies the return value of the function

Pea resource search sitehttps://55wd.com Computer Embroidery Embroidery Factory ttp://www.szhdn.com

Currying

Collect parameters and delay execution, also known as partial evaluation
function add(a, b) {
    return a + b
}

// Simple generic package
function curry(fn) {
    let args = Array.prototype.slice.call(arguments, 1)
    return function() {
        let _args = Array.prototype.slice.call(arguments)
        let final = args.concat(_args)
        return fn.apply(null, final)
    }
}

// Curry the function add
let adder = curry(add)
adder(1, 2)
// or
let adder = curry(add, 1)(2)
let adder = curry(add)(1, 2)

A typical general-purpose curry package

Function.prototype.mybind = function(fn) {
    let args = Array.prototype.slice(arguments, 1)
    let _this = this
    return function() {
        let _args = Array.prototype.slice(arguments)
        let final = args.concat(_args)
        return _this.apply(fn, final)
    }
}

Through this pattern of curry functions, we can implement a simple bind

Function.prototype.mybind = function(fn) {
    let _this = this
    return function() {
        return _this.apply(fn, arguments)
    }
}

Function currying is also a problem we may often encounter during the interview process, such as:

// Write an add function that implements the following functions
add(1)(2)(3) // 6
add(1)(2, 3)(4) //10
add(1, 2)(3) (4, 5) // 15

function add() {
    let args = Array.prototype.slice.call(arguments)
    let adder =  function() {
        // Use closures to save args and collect parameters
        args = args.concat(Array.prototype.slice.call(arguments))
        return adder
    }
    // Returns the final computed value using the toString feature of implicit conversion
    adder.toString = function() {
        return args.reduce((a, b) => {
            return a + b
        })
    }
    return adder
}
add(1)(2)(3) // 6
add(1)(2, 3)(4) // 10
add(1, 2)(3)(4, 5) // 15

// Of course, we can also simplify this function with the help of ES6 methods
function add1(...args) {
    let adder = (..._args) => {
        args = [...args, ..._args]
        return adder
    }
    adder.toString = () => args.reduce((a, b) => a + b)
    return adder
}

To achieve the effect of the above function, I think there are two points that we must understand and master:

  • Closures, use the characteristics of closures to save and collect the parameters we need
  • Using the implicit conversion feature of toString, we finally get the result we want a

Tags: Javascript

Posted by wtech on Mon, 16 May 2022 20:22:13 +0300