Understanding the closure, higher order function and currying using JS

Source: Internet
Author: User
Tags closure mul

One, closure 1. The concept of closures

Closures are closely related to the execution context, environment, scope

Execution context

The execution context is a canonical device used to track run-time code evaluation, and logically, the execution context is maintained with the execution context stack (stack, call stack).

There are several types of code: Global Code, function code, eval code, and module code, each of which is evaluated in the context of its execution.

When the function is called, a new execution context is created and pressed into the stack-at which point it becomes an active execution context. When the function returns, this context is ejected from the stack

function recursive (flag) {   //Exit condition.  if (flag = = = 2) {    return;  }   Call recursively.  Recursive (++flag);} Go.recursive (0);

The context in which another context is called is called the Caller (caller). The invoked context is appropriately referred to as the callee (callee), in which recursive is both the caller and the callee

The corresponding execution context stack

Typically, the code for a context runs until the end. However, in the generator of asynchronous processing, it is special.

A generator function may suspend its executing context and remove it from the stack before it is finished. Once the generator is activated again, its context is restored and pressed into the stack again

function *g () {    yield 1;    Yield 2;        } var f = g (); F.next (); F.next ();

yield The statement returns the value to the caller and pops the context. When you call next, the same context is pressed back into the stack, and the

Environment

There is an associated lexical environment for each execution context

You can define a lexical environment as a repository for variables, functions, and classes in scope, and each environment has a reference to an optional parent environment

For example, the global context in this code corresponds to the context of the Foo function.

Let x = 10;let y = 20; function foo (z) {let  x = +;  return x + y + z;} Foo (30); 150

Scope

When an execution context is created, it is associated with a specific scope (the code domain realm). This scope provides a global environment for the context (this "global" is not global in the general sense, but a means of providing context stack calls)

Static scope

If a language can determine the context in which the binding is resolved by looking only for source code, then the language implements a static scope. Therefore, the general can also be called the lyrics law scope.

The function is referenced in the environment, and the function is referenced by the environment. Static scopes are implemented by capturing the environment in which the function is created.

, the Global environment references the Foo function, and the Foo function also refers to the global environment

Free variables

A variable that is neither a formal parameter of a function nor a local variable of a function

function Testfn () {   var localVar = ten;   function Innerfn (innerparam) {    alert (Innerparam + LocalVar);  }   return INNERFN;}

For the INNERFN function, LocalVar belongs to the free variable

Closed Package

A closure is a combination of code blocks and data in the context in which the code block is created, which is the environment in which the function captures the definition (closed environment).

In JS, the function is a class-one citizen (first-class), in general the code block is the meaning of the function (temporarily do not consider the special circumstances of ES6)

So, a closure is not just a function, it is an environment in which some relevant data and pointers are stored.

In theory, all functions are closures.

Because they are all created, the data in the upper context is saved. This is true even for simple global variables, because accessing a global variable in a function is equivalent to accessing a free variable, at which point the outermost scope is used.

From the point of view of implementation, not fully follow the theory, but also two points, according to one can be called closure

A free variable is referenced in the code

The context in which it was created has been destroyed, and it still exists (for example, an intrinsic function is returned from the parent function)

More relevant concepts to view this series

2. Characteristics of closures
    • function nesting functions
    • External parameters and variables can be referenced inside the function
    • Parameters and variables are not recycled by the garbage collection mechanism

In general, closures have nested functions that refer to external parameters and variables (free variables) and persist after their context is destroyed (not recycled by the garbage collection mechanism)

3. Advantages of closures
    • Keep a variable in memory for a long time
    • Avoid contamination of global variables
    • Existence as a private member

According to the characteristics, closures have the corresponding advantages

For example, to create a counter, as a general rule we can use the class

function Couter () {    this.num = 0;} Couter.prototype = {    Constructor:couter,        //Increment    up:function () {        this.num++;    },        //Minus down    : function () {        this.num--;    },        //Get    getnum:function () {        console.log (this.num);    }}; var C1 = new Couter (); C1.up (); C1.up (); C1.getnum (); 2var C2 = new Couter (); C2.down (); C2.down (); C2.getnum (); -2

This is good, we can also use the closure of the way to achieve

function Couter () {    var num = 0;    return {        //increment        up:function () {            num++;        },        //minus        down:function () {            num--;        },        //Get        getnum:function () {            console.log (num);}}    ;} var C1 = Couter (); C1.up (); C1.up (); C1.getnum (); 2var C2 = Couter (); C2.down (); C2.down (); C2.getnum (); -2

As you can see, num is still in memory while the context of the Couter function is destroyed

In many design patterns, closures act as important roles,

4. Disadvantages of closures

The downside of closures is more in terms of memory performance.

Due to the long-term presence of variables in memory, there may be insufficient memory in complex programs, but this is not very serious, we need to make a choice in memory usage and development methods. Clean out variables when you don't need them.

At some point (the object is referenced to the DOM, the GC uses reference counting) to cause a memory leak, remember to clean up the variable before exiting the function

Window.onload = function () {     var elem = document.queryselector ('. txt ');          Elem's onclick points to the anonymous function, and the closure of the anonymous function also refers to the elem     Elem.onclick = function () {          console.log (this.innerhtml);     };     Clean     elem = null;};    

Memory leaks related things, there is not much to say, and then to organize an article

In addition, since the variables in the closure can be modified outside the function (through the exposed interface method), all inadvertently also internal variables will be modified, so also pay attention to

5. Application of closures

Closures have a wide range of usage scenarios

A common question is, what does this piece of code output

var func = [];for (var i = 0; i < 5; ++i) {    Func[i] = function () {        console.log (i);    }} FUNC[3] (); 5

Since the scope of the relationship, the final output of 5

With a slight modification, you can use anonymous functions to execute and close the packet immediately to output the correct result

for (var i = 0; i < 5; ++i) {    (function (i) {        Func[i] = function () {            console.log (i);        }     }) (i);  } FUNC[3] (); 3for (var i = 0; i < 5; ++i) {    (function () {        var n = i;        Func[i] = function () {            console.log (n);        }     }) ();  } FUNC[3] (); 3for (var i = 0; i < 5; ++i) {    Func[i] = (function (i) {        return function () {            console.log (i);        }    }) (i);} FUNC[3] (); 3

Second, higher order function

High-order functions (High-order function Abbreviation: HOF), I sound so advanced, meet the following two points can be called higher-order functions

    • function can be passed as a parameter
    • function can be output as a return value

The definition in Wiki is

    • Accept one or more functions as input
    • Output a function

Higher-order functions can be understood as functions above functions , which are commonly used, such as common

var getData = function (URL, callback) {    $.get (URL, function (data) {        callback (data);    });}

Or you can use it in many closed scenarios.

such as the anti-shake function (debounce) and throttling function (throttle)

Debounce

Anti-shake, which means that no matter how many times an action is triggered, it will not be executed until the continuous action is stopped.

For example, an input box accepts the user input, and the input is completed before the search begins.

With page scrolling as an example, you can define a shake-out function that accepts a custom delay value as the time identifier for the stop.

function stabilization, which is not processed in frequent operation until the operation is completed (again delay time) only once processing function debounce (FN, delay) {    delay = Delay | |        var timer = null;    return function () {        var arg = arguments;                  The last timer        cleartimeout (timer) is cleared during each operation;        timer = null;                Define a new timer, operate after a period of time        timer = setTimeout (function () {            fn.apply (this, arg);        }, delay);}    }; var count = 0;window.onscroll = Debounce (function (e) {    console.log (E.type, ++count);//scroll}, 500);

Scrolling the page, you can see that only after scrolling is done

Throttle

Throttling, which means that no matter how many times an action is triggered, it can only trigger one time within a defined period.

For example, resize and scroll time frequently triggered operations, if all accepted processing, may affect performance, need throttling control

With page scrolling as an example, you can define a throttling function that accepts a custom delay value as the time indicator for the stop.

Two points to be aware of

To set an initial identity, prevent it from being executed at the beginning of the process, and reset after the last processing

Also set timer processing, prevent two action not to delay value, the last set of actions can not trigger

function throttling, the time of the interval delay in the frequent operation is only processed once function throttle (FN, delay) {    delay = Delay | |        var timer = null;    Each scrolling initial identification    var timestamp = 0;    return function () {        var arg = arguments;        var now = Date.now ();                Set start time        if (timestamp = = = 0) {            timestamp = now;        }                Cleartimeout (timer);        timer = null;                has been to the delay for a period of time, to be processed        if (now-timestamp >= delay) {            fn.apply (this, arg);            timestamp = now;        }        Add timers to ensure that the last operation can also handle        else {            timer = setTimeout (function () {                fn.apply (this, arg);                Recover identity                timestamp = 0;            }, delay);}}    ; var count = 0;window.onscroll = Throttle (function (e) {    console.log (E.type, ++count);//scroll}, 500);

Third, currying

The currying, also known as partial evaluation, is a function that transforms a function that accepts multiple parameters into one that accepts a single parameter (the first parameter of the initial function), and returns a new function, which takes the remaining parameters and returns the result of the operation.

A more classic example is

Implementation cumulative Add (1) (2) (3) (4)

The first approach is to use callback nesting

function Add (a) {    //Mad Callback    return function (b) {        return function (c) {            return function (d) {                   //return a + B + C + D;                   return [A, B, C, d].reduce (function (v1, v2) {                       return v1 + v2;                   });            }        }    } Console.log (Add (1) (2) (3) (4)); 10

Neither graceful nor well-extended

Modify two, let IT support variable number of parameters

function Add () {    var args = [].slice.call (arguments);        Used to store update parameter array    function adder () {        var arg = [].slice.call (arguments);        args = Args.concat (arg);                Each call, return itself, the value can be obtained by the internal ToString value        return adder;    }        Specifies the value of toString for the implicit value calculation    adder.tostring = function () {        return args.reduce (function (v1, v2) {            return v1 + V 2;        });    return adder;} Console.log (Add (1, 2), add (1, 2) (3), add (1) (2) (3) (4)); 3 6 10

The above code will enable this "curry"

Two points to be aware of are

    • Arguments is not a real array, so you cannot use an array's native method (such as slice)
    • When the value is taken, the implicit evaluation is performed first by the internal ToString (), then by ValueOf (), the valueOf priority is higher, we can override the initial method

Of course, not all types of ToString and Tovalue are the same, number, String, Date, Function types are not exactly the same, this article does not expand

It uses the call method, which is mainly to change the context of execution, similar to Apply,bind, etc.

We can try to customize the bind method of a function, such as

var obj = {    num:10,    getnum:function (num) {        console.log (num | | this.num);}    }; var o = {    num:20};obj.getnum ();//10obj.getnum.call (o, n);//1000obj.getnum.bind (O) (20);//20//custom bind bind fun Ction.prototype.binder = function (context) {    var fn = this;    var args = [].slice.call (arguments, 1);    return function () {        return fn.apply (context, args);    };};o Bj.getNum.binder (o, 100) (); 100

The above currying is not perfect, if you want to define a multiplication function, you have to write again long code

A generic currying function needs to be defined as a wrapper

currying function Curry (FN) {    var args = [].slice.call (arguments, 1);        function inner () {        var arg = [].slice.call (arguments);        args = Args.concat (arg);        return inner;    }    inner.tostring = function () {        return fn.apply (this, args);    };    return inner;} function Add () {    return [].slice.call (arguments). reduce (function (v1, v2) {        return v1 + v2;    });} function Mul () {    return [].slice.call (arguments). reduce (function (v1, v2) {        return v1 * v2;    });} var curryadd = Curry (add), Console.log (Curryadd (1) (2) (3) (4) (5)); 15var Currymul = Curry (mul, 1); Console.log (Currymul (2, 3) (4) (5)); 120

It looks a lot better, it's easy to expand

In practice, however, in the application of curry, the number of parameter scenarios is less and the parameters are fixed in more cases (common two or three)

currying function Curry (FN) {    var args = [].slice.call (arguments, 1),        //functions fn parameter length        fnlen = fn.length;        Stores the parameter array until the parameter is sufficient, calling    function inner () {        var arg = [].slice.call (arguments);        args = Args.concat (arg);        if (args.length >= fnlen) {            return fn.apply (this, args);        } else {            return inner;        }    }    return inner;} function Add (A, B, C, D) {    return a + B + C + D;} function Mul (A, B, C, D) {    return a * b * c * D;} var curryadd = Curry (add), Console.log (Curryadd (1) (2) (3) (4)); 10var Currymul = Curry (mul, 1); Console.log (Currymul (2, 3) (4)); 24

In the Add method defined above, 4 parameters are accepted

In our currying function, accept the Add method and remember how many parameters this method needs to accept, store the incoming parameters, and call processing when the quantity requirement is met.

Anti-currying

Anti-Curry, which reverses the function of the curry after it is converted from several calls that accept a single parameter to a single call that takes multiple arguments

One simple way to do this is to pass multiple parameters to the function of the curry one at a time, because our curry function natively supports the incoming processing of multiple parameters, and only "one call" is used when the anti-Curry call is invoked.

Combining the above curry code, the anti-curry code is as follows

Anti-currying function Uncurry (FN) {    var args = [].slice.call (arguments, 1);    return function () {        var arg = [].slice.call (arguments);                args = Args.concat (arg);        Return fn.apply (this, args);    }} var uncurryadd = Uncurry (Curryadd); Console.log (Uncurryadd (1, 2, 3, 4)); 10var Uncurrymul = Uncurry (Currymul, 2); Console.log (Uncurrymul (3, 4)); 24

Resources:

Javascript. The core:2nd Edition

JavaScript: Core-second edition (translated)

Ecma-262-3 in detail. Chapter 6. Closures.

Understanding the closure, higher order function and currying using JS

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.