A function decorator is a higher-order function that takes one function as an argument and returns another function that is a variant of the argument function.

The best way to improve your programming skills is to read and learn about open source frameworks or script libraries. Today we’ll learn about how underscore. Js, Lodash.js, ramda.js and other libraries use closure principles to implement function modifiers.

A combination of a function bound to (or surrounded by) references to its surrounding state (lexical environment) is a closure. That is, closures allow you to access the scope of an outer function within an inner function. In Javascript, whenever a function is created, the closure is created at the same time the function is created.

once()

A similar function once() was written in a previous article. But here it does something a little different, executing once and returning the result of the function’s execution.

function once(fn) {
    let returnValue;
    let canRun = true;
    return function runOnce() {
        if (canRun) {
            returnValue = fn.apply(this, arguments);
            canRun = false;
        }
        return returnValue;
    };
}
function process(title) {
    console.log({
        title,
    });
}
const processonce = once(process);
const title = "DevPoint";
processonce(title);
processonce(title);
processonce(title);
Copy the code

{title: ‘DevPoint’}

Once () is a function that returns another function. The returned function runOnce() is a closure. It is also important to note how the original function is called — by passing in the current value of this and all arguments: fn.apply(this, arguments). Can be applied to the scene can be panic buying, such as now more activities grab Maotai, can reduce the crazy click to send requests.

after()

After (count, fn) : Creates a function method that is executed only after multiple calls. This function is useful, for example, when you want to ensure that the function is run only after the count asynchronous operation is complete.

function after(count, fn) { let runCount = 0; return function runAfter() { runCount = runCount + 1; if (runCount >= count) { return fn.apply(this, arguments); }}; } function end() {console.log(" Asynchronous operation is over!" ); } const endAfter3Calls = after(3, end); LogResult setTimeout(() => {console.log("=> complete the first asynchronous operation "); endAfter3Calls(); }, 3000); SetTimeout (() => {console.log("=> complete the second asynchronous operation "); endAfter3Calls(); }, 2000); SetTimeout (() => {console.log("=> complete the third asynchronous operation "); endAfter3Calls(); }, 6000);Copy the code

The output of the above code execution is as follows:

=> Complete the second asynchronous operation => Complete the first asynchronous operation => Complete the third asynchronous operation. The asynchronous operation is complete!Copy the code

This method is useful when the front end needs to do a series of animations.

Throttling: throttle ()

Throttle (FN, wait) : Throttle (FN, wait) : specifies the number of times a function is triggered. You can execute the function only once per delay interval. The most common example is when you are listening to resize/scroll events and you need to throttle the callback frequency for performance reasons.

function throttle(fn, interval) {
    let lastTime;
    return function throttled() {
        const timeSinceLastExecution = Date.now() - lastTime;
        if (!lastTime || timeSinceLastExecution >= interval) {
            fn.apply(this, arguments);
            lastTime = Date.now();
        }
    };
}
function process() {
    console.log("DevPoint");
}
const throttledProcess = throttle(process, 1000);

for (let i = 0, len = 100; i < len; i++) {
    throttledProcess();
}
Copy the code

Stabilization: debounce ()

Debounce (fn, wait) : You can reduce the frequency with which the function fires, but you limit it in a slightly different way. When a function fires, a timer is used to delay the execution of the operation. When the function is triggered again, the timer is cleared and reset. If the last delayed operation has not been executed, it is cleared.

function debounce(fn, interval) {
    let timer;
    const debounced = () => {
        clearTimeout(timer);
        const args = arguments;
        timer = setTimeout(() => {
            fn.apply(this, args);
        }, interval);
    };
    return debounced;
}
function process() {
    console.log("DevPoint");
}
const delayProcess = debounce(process, 400);

for (let i = 0, len = 100; i < len; i++) {
    delayProcess();
}
Copy the code

The difference between throttle and debounce is that throttle executes as soon as it fires, whereas debounce executes after a certain delay. The function is executed only once, from the start of firing to the end of the delay.

partial()

Now create a partial() method that is available to all functions. The ECMAScript 6 REST parameter syntax is used here… Instead of arguments objects, the following implementation concatenates arrays and arguments instead of array objects.

Function.prototype.partial = function (... leftArguments) { let fn = this; return function partialFn(... rightArguments) { let args = leftArguments.concat(rightArguments); return fn.apply(this, args); }; }; Function log(level, message) {console.log(level + ":" + message); } const logInfo = log.partial(" description "); LogInfo ("DevPoint development Technical essentials ");Copy the code

Implementing these common functions can help us better understand how a decorator works and give us an idea of the types of logic it can encapsulate.

Function modifiers are a powerful tool for creating variations of existing functions without modifying the original function. They can be used as part of a functional programming toolkit to reuse common logic.