What is a 'Closure'?

2018-12-31 16:24发布

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

16条回答
无与为乐者.
2楼-- · 2018-12-31 16:30

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.

查看更多
与风俱净
3楼-- · 2018-12-31 16:37

First of all, contrary to what most of the people here tell you, closure is not a function! So what is it?
It is a set of symbols defined in a function's "surrounding context" (known as its environment) which make it a CLOSED expression (that is, an expression in which every symbol is defined and has a value, so it can be evaluated).

For example, when you have a JavaScript function:

function closed(x) {
  return x + 3;
}

it is a closed expression because all the symbols occurring in it are defined in it (their meanings are clear), so you can evaluate it. In other words, it is self-contained.

But if you have a function like this:

function open(x) {
  return x*y + 3;
}

it is an open expression because there are symbols in it which has not been defined in it. Namely, y. When looking at this function, we can't tell what y is and what does it mean, we don't know its value, so we cannot evaluate this expression. I.e. we cannot call this function until we tell what y is supposed to mean in it. This y is called a free variable.

This y begs for a definition, but this definition is not part of the function – it is defined somewhere else, in its "surrounding context" (also known as the environment). At least that's what we hope for :P

For example, it could be defined globally:

var y = 7;

function open(x) {
  return x*y + 3;
}

Or it could be defined in a function which wraps it:

var global = 2;

function wrapper(y) {
   var w = "unused";

   return function(x) {
     return x*y + 3;
   }

}

The part of the environment which gives the free variables in an expression their meanings, is the closure. It is called this way, because it turns an open expression into a closed one, by supplying these missing definitions for all of its free variables, so that we could evaluate it.

In the example above, the inner function (which we didn't give a name because we didn't need it) is an open expression because the variable y in it is free – its definition is outside the function, in the function which wraps it. The environment for that anonymous function is the set of variables:

{
  global: 2,
  w: "unused",
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Now, the closure is that part of this environment which closes the inner function by supplying the definitions for all its free variables. In our case, the only free variable in the inner function was y, so the closure of that function is this subset of its environment:

{
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

The other two symbols defined in the environment are not part of the closure of that function, because it doesn't require them to run. They are not needed to close it.

More on the theory behind that here: https://stackoverflow.com/a/36878651/434562

It's worth to note that in the example above, the wrapper function returns its inner function as a value. The moment we call this function can be remote in time from the moment the function has been defined (or created). In particular, its wrapping function is no longer running, and its parameters which has been on the call stack are no longer there :P This makes a problem, because the inner function needs y to be there when it is called! In other words, it requires the variables from its closure to somehow outlive the wrapper function and be there when needed. Therefore, the inner function has to make a snapshot of these variables which make its closure and store them somewhere safe for later use. (Somewhere outside the call stack.)

And this is why people often confuse the term closure to be that special type of function which can do such snapshots of the external variables they use, or the data structure used to store these variables for later. But I hope you understand now that they are not the closure itself – they're just ways to implement closures in a programming language, or language mechanisms which allows the variables from the function's closure to be there when needed. There's a lot of misconceptions around closures which (unnecessarily) make this subject much more confusing and complicated that it actually is.

查看更多
看风景的人
4楼-- · 2018-12-31 16:39

Closures Whenever we have a function defined inside another function, the inner function has access to the variables declared in the outer function. Closures are best explained with examples. In Listing 2-18, you can see that the inner function has access to a variable (variableInOuterFunction) from the outer scope. The variables in the outer function have been closed by (or bound in) the inner function. Hence the term closure. The concept in itself is simple enough and fairly intuitive.

Listing 2-18:
    function outerFunction(arg) {
     var variableInOuterFunction = arg;

     function bar() {
             console.log(variableInOuterFunction); // Access a variable from the outer scope
     }
     // Call the local function to demonstrate that it has access to arg
     bar(); 
    }
    outerFunction('hello closure!'); // logs hello closure!

source: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf

查看更多
心情的温度
5楼-- · 2018-12-31 16:41

I'll give an example (in JavaScript):

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

What this function, makeCounter, does is it returns a function, which we've called x, that will count up by one each time its called. Since we're not providing any parameters to x it must somehow remember the count. It knows where to find it based on what's called lexical scoping - it must look to the spot where it's defined to find the value. This "hidden" value is what is called a closure.

Here is my currying example again:

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

What you can see is that when you call add with the parameter a (which is 3), that value is contained in the closure of the returned function that we're defining to be add3. That way, when we call add3 it knows where to find the a value to perform the addition.

查看更多
素衣白纱
6楼-- · 2018-12-31 16:41

A closure is a function that can reference state in another function. For example, in Python, this uses the closure "inner":

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
查看更多
情到深处是孤独
7楼-- · 2018-12-31 16:41

Functions containing no free variables are called pure functions.

Functions containing one or more free variables are called closures.

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo 
  // foo is a free variable from the outer environment
}

src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure

查看更多
登录 后发表回答