1

I have read another question on stackoverflow, he said:

  • A closure is one way of supporting first-class functions; it is an expression that can reference variables within its scope (when it was first declared), be assigned to a variable, be passed as an argument to a function, or be returned as a function result.
  • Or, a closure is a stack frame which is allocated when a function starts its execution, and not freed after the function returns (as if a 'stack frame' were allocated on the heap rather than the stack!).

But I have also read MDN, it said:

A closure is the combination of a function and the lexical environment within which that function was declared.

I think they are totally different. But if MDN is right,

var a=1;
function printA(){
    console.log(a);
}

this code include a function printA() and its lexical environment(variable a), does it mean this code is closure? Or MDN is wrong?

Lasy
  • 97
  • 9
  • Hard to answer it precisely, the above code snippet might or might not force the JS engine to create a closure depending on the given circumstances. But generally yes, `printA` would close over `a` in your example. – Matus Dubrava May 25 '18 at 12:43
  • *printA* has a closure to the outer *a* variable. Access to *a* can also be explained in terms of identifier resolution on the scope chain. – RobG May 25 '18 at 12:44
  • 1
    That "*a closure is a stack frame*" explanations seems totally off. – Bergi May 25 '18 at 13:14

2 Answers2

8

MDN is correct, a closure is the combination of a function and its reference to its external environment (which allows it to access the contents of that environment). See also the Wikipedia entry. (Loosely speaking, a closure is a function that has a reference to its outer environment.)

The text you've quoted from elsewhere on SO seems slightly off, but concepts are hard to explain.

But if MDN is right,

var a=1;
function printA(){
    console.log(a);
}

this code include a function printA() and its lexical environment(variable a), does it mean this code is closure?

Yes. In fact, all functions in JavaScript are closures (or perhaps more precisely, all functions are associated with closures). That's how global variables work, because all functions are closures over the global lexical environment.


As an implementation optimization, a JavaScript engine can sometimes optimize away the function's link to the external lexical environment, such as here:

function double(a) {
    return a * 2;
}

Nothing in double refers to any identifer that isn't defined within the lexical environment created for the call to double, so smart engines can (and do) optimize away the link entirely. Other times, they can determine that only parts of the external environment are used and so only retain those, allowing others to be garbage collected.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Closure is a **combination** but no only a **function**, so I think it is a bit inappropriate to say a function is a closure. – Lasy May 26 '18 at 00:55
  • @Lasy - As I said, "loosely." But it's also literally true specifically in JavaScript; the [[Environment]] link is an intrinsic part of the function in JavaScript, just as much as its parameter list and code: https://tc39.github.io/ecma262/#table-27 – T.J. Crowder May 26 '18 at 06:55
  • I am confused. A function include Lexical Environment, so the combination of a function and the lexical environment equals to a functionn. A closure is a function? – Lasy May 26 '18 at 07:38
  • @Lasy - Conceptually, a closure is the combination of a function and the environment it's bound to. *In JavaScript* all functions are closures, and the link is built into them.. That may not be true in other languages. – T.J. Crowder May 26 '18 at 07:46
  • @T.J. Crowder - "a closure is a function that has a reference to its outer environment" - If this is the case, then what's the difference between `scope chain` and a `closure`? – darKnight Apr 19 '20 at 15:17
  • @maverick - It depends on what you mean by "scope chain," the specification doesn't use the term and I've heard it used for different things. *One* the things I've heard it used for is to refer to the link an environment has to its outer environment (used for identifier resolution). If that's the meaning your'e using, the biggest difference btw that and a closure is that there isn't always a function involved in the links in the "scope chain," because various structures (including, as of ES2015, blocks) create nested environments without a function being involved. So they're related, ... – T.J. Crowder Apr 19 '20 at 15:57
  • ... but different. You could think of a closure as a function with a link to the "top" of the scope chain (as far as that function is concerned). Suppose you had functions `foo` and `bar` in module `x`, and `foo` refers to both `bar` and `console` (a global). Since `foo` is a closure over the module environment, when it looks up `bar` it finds it in the environment `foo` has a direct link to. But when `foo` looks up `console`, it doesn't find it in the module environment, so it has to follow the module's link to its outer environment (the global environment) in order to find it. – T.J. Crowder Apr 19 '20 at 15:57
  • @T.J.Crowder - By `scope chain`, I mean the hierarchy of `execution contexts` that are created during program execution. And as far as the ecmascript specification is concerned, it doesn't give any formal definition for `closure` either, though it does use the word in some places. – darKnight Apr 19 '20 at 16:27
  • @maverick - Indeed, it doesn't. :-) Note that *execution contexts* aren't linked together, [*lexical environments*](https://tc39.es/ecma262/#sec-executable-code-and-execution-contexts) are. Those are the environments I'm referring to above. So yes, we're using the same meaning of "scope chain." – T.J. Crowder Apr 19 '20 at 16:41
  • @T.J.Crowder `Execution contexts` aren't linked together, but the Javascript engine "looks through" these when trying to look up a variable. So a variable used in a function will be looked up in its function execution context first, then its parent function execution context..till the global execution context is reached, unless the variable is found earlier. So I believe this is different than the 'lexical environment' you are referring to. So I believe "scope chain" and closures are different. Though your answer makes more sense now. – darKnight Apr 19 '20 at 16:53
  • @maverick - Read through the spec starting [here](https://tc39.es/ecma262/#sec-resolvebinding). I mean, it's not like it matters much, but it's the lexical environments, not the execution contexts, that it looks through for identifier resolution. (The lexical environments are often ones associated with an execution context, though.) Yes, "scope chain" and closures are different, as I said above. – T.J. Crowder Apr 19 '20 at 17:16
  • @T.J.Crowder Went through the docs. Every `execution context` actually _contains_ a `Lexical environment`, which in turn contains an `environment record`, which records the identifier bindings that are created within the scope of its associated Lexical Environment. So, I guess we both were basically referring to the same thing :) – darKnight Apr 19 '20 at 17:42
  • @maverick - Right, exactly -- but it's not a 1:1 mapping, you can have lexical environment records that **aren't** (directly) tied to a specific execution context, which is the point I was making with the distinction. That's why it's the lexical environments, not the execution contexts, that are linked together and used for identifier resolution. – T.J. Crowder Apr 19 '20 at 17:45
  • @T.J.Crowder Interesting. Curious to know in which case the execution context might not have any lexical environment records? – darKnight Apr 19 '20 at 17:48
  • @maverick - It's the other way around, you can have multiple lexical environments within an execution context -- as I mentioned above, some constructs (including blocks in ES2015+) create another lexical environment in the same execution context.. – T.J. Crowder Apr 19 '20 at 17:52
  • @T.J.Crowder I was hoping that you would give an example of a lexical environment which _isn't_ linked to any execution context, as you said might be the case sometimes. – darKnight Apr 19 '20 at 17:59
  • @maverick - No, I didn't say that, I said not *directly* linked. Please read the comments above carefully, I think I've been fairly clear. For example, a block (as of ES2015) gets its own lexical environment. You can see the mechanics of it [here](https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation). The main point being: If you're interested in identifier resolution, it's the environments, not the contexts, that matter. Happy spec delving! – T.J. Crowder Apr 19 '20 at 18:02
  • @T.J.Crowder I am not sure what you mean by lexical environment not being _directly_ linked to any execution context. I don't quite understand how one would differentiate between _direct_ and _indirect_ linking. – darKnight Apr 19 '20 at 18:10
  • @maverick - Please see the linked spec section. Just for clarity: It starts by getting the lexenv for the execution context (`oldEnv`). Then it creates a new lexenv (`blockEnv`) and sets *that* as the lexenv for the execution context. Now, the previous lexenv isn't directly inked to an execution context anymoer, it's only indirectly linked to it (via `blockEnv`). Eventually we leave the block and it sets the old one back again, but during the time execution is in the block, the old lexenv isn't directly linked to an execution context -- but is part of what we've called the "scope chain." :-) – T.J. Crowder Apr 19 '20 at 18:44
0

It's a closure, and here's why. Suppose that you have this code on your page:

var a = 1;
function printA(){
    console.log(a);
}

Now imagine that your page contains an <iframe>. It's served from the same origin, so it has access to its parent window. Suppose that the page in your <iframe> contains the following code:

var a = 2;
self.parent.printA();

It would print 1 to the console, because even though your <iframe>'s script also defines a variable named a, the funciton printA() sees the a that was defined in its lexical context.

Máté Safranka
  • 4,081
  • 1
  • 10
  • 22