What is a 'Closure'?

前端 未结 23 1212
星月不相逢
星月不相逢 2020-11-22 08:02

I asked a question about Currying and closures were mentioned. What is a closure? How does it relate to currying?

23条回答
  •  礼貌的吻别
    2020-11-22 09:01

    tl;dr

    A closure is a function and its scope assigned to (or used as) a variable. Thus, the name closure: the scope and the function is enclosed and used just like any other entity.

    In depth Wikipedia style explanation

    According to Wikipedia, a closure is:

    Techniques for implementing lexically scoped name binding in languages with first-class functions.

    What does that mean? Lets look into some definitions.

    I will explain closures and other related definitions by using this example:

    function startAt(x) {
        return function (y) {
            return x + y;
        }
    }
    
    var closure1 = startAt(1);
    var closure2 = startAt(5);
    
    console.log(closure1(3)); // 4 (x == 1, y == 3)
    console.log(closure2(3)); // 8 (x == 5, y == 3)

    First-class functions

    Basically that means we can use functions just like any other entity. We can modify them, pass them as arguments, return them from functions or assign them for variables. Technically speaking, they are first-class citizens, hence the name: first-class functions.

    In the example above, startAt returns an (anonymous) function which function get assigned to closure1 and closure2. So as you see JavaScript treats functions just like any other entities (first-class citizens).

    Name binding

    Name binding is about finding out what data a variable (identifier) references. The scope is really important here, as that is the thing that will determine how a binding is resolved.

    In the example above:

    • In the inner anonymous function's scope, y is bound to 3.
    • In startAt's scope, x is bound to 1 or 5 (depending on the closure).

    Inside the anonymous function's scope, x is not bound to any value, so it needs to be resolved in an upper (startAt's) scope.

    Lexical scoping

    As Wikipedia says, the scope:

    Is the region of a computer program where the binding is valid: where the name can be used to refer to the entity.

    There are two techniques:

    • Lexical (static) scoping: A variable's definition is resolved by searching its containing block or function, then if that fails searching the outer containing block, and so on.
    • Dynamic scoping: Calling function is searched, then the function which called that calling function, and so on, progressing up the call stack.

    For more explanation, check out this question and take a look at Wikipedia.

    In the example above, we can see that JavaScript is lexically scoped, because when x is resolved, the binding is searched in the upper (startAt's) scope, based on the source code (the anonymous function that looks for x is defined inside startAt) and not based on the call stack, the way (the scope where) the function was called.

    Wrapping (closuring) up

    In our example, when we call startAt, it will return a (first-class) function that will be assigned to closure1 and closure2 thus a closure is created, because the passed variables 1 and 5 will be saved within startAt's scope, that will be enclosed with the returned anonymous function. When we call this anonymous function via closure1 and closure2 with the same argument (3), the value of y will be found immediately (as that is the parameter of that function), but x is not bound in the scope of the anonymous function, so the resolution continues in the (lexically) upper function scope (that was saved in the closure) where x is found to be bound to either 1 or 5. Now we know everything for the summation so the result can be returned, then printed.

    Now you should understand closures and how they behave, which is a fundamental part of JavaScript.

    Currying

    Oh, and you also learned what currying is about: you use functions (closures) to pass each argument of an operation instead of using one functions with multiple parameters.

提交回复
热议问题