1

I am learning closure and have basic doubt.

function makeCounter() {
  let count = 0;

  return function() {
    return count++;
  };
}

let counter = makeCounter();

Here when we are returning function it has closure with outer scope. so it has a closure with count.

var x = 1;

function func() {
  console.log(x); //  does func has closure with x ??

}

func();

So my question is , only nested function makes closure ?? or top level func has also closure with x ??

piyush
  • 33
  • 3
  • Top level functions don't create closures; you can still access `x` outside of `func` in your second example. – kelsny Oct 28 '22 at 16:05
  • 2
    Source please, @caTS. – Ben Aston Oct 28 '22 at 16:08
  • https://stackoverflow.com/questions/35130415/is-every-function-a-closure – Andy Oct 28 '22 at 16:10
  • @piyush There may be confusion what you're asking. Are you asking: 1) Does `func()` have access to global `x`? Or 2) Does the value of `x` change in `func()` if it's later changed elsewhere? Or 3) Something else. — Looks like "closure" is a complex concept. And possibly I think of it too simply—variable and function protection. – bloodyKnuckles Oct 28 '22 at 17:41

4 Answers4

1

Closures in JavaScript are implemented using the [[OuterEnv]] internal field of the [[EnvironmentRecord]] internal field of function declarations. The [[OuterEnv]] value is then copied to the execution context created any time a function is called. This value is then used to create the scope chain, enabling the closure behavior.

Functions declared in the global context are no different in this regard and maintain a link to their surrounding environment, the Global Environment Record.

In the global context, variables declared using var and function declarations behave subtly differently to those declared inside other functions, in that they end up as properties on the global object, but this is an implementation detail.

Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • I think of closures as limiting access to variables and functions. Is there more to closures than simply that? – bloodyKnuckles Oct 28 '22 at 16:34
  • A closure is simply a link between a function declaration and its surrounding lexical environment. – Ben Aston Oct 28 '22 at 16:35
  • Even function declarations nested in function declarations, ad infinitum, have access to the global environment. What practical benefit is there to a link to the surrounding lexical environment? – bloodyKnuckles Oct 28 '22 at 16:52
  • Creation of the scope chain. – Ben Aston Oct 28 '22 at 17:02
  • To clarify: access to that global environment, from within all functions, is enabled by this very mechanism. – Ben Aston Oct 28 '22 at 17:09
  • function makeCounter() { let count = 0; var test = function() { return count++; }; return test } let counter = makeCounter(); in the above example, test function has three element in scope array> global, script and closure. var x = 1; function func() { console.log(x); // does func has closure with x ?? } func(); in above example func does not clousre property in their scope array. @ Ben Aston can you explain this behavior ? – piyush Oct 29 '22 at 03:13
  • Scope is not an array. Chrome dev tools has a proprietary way of rendering the contents of the scope chain. Scope is a chain of Environment Records, each containing the identifiers declared in that local scope. Environment Records form a chain, linked by their `[[OuterEnv]]` fields and terminating at the global environment record. – Ben Aston Oct 29 '22 at 15:30
  • See also: https://stackoverflow.com/a/70786086/38522 – Ben Aston Oct 29 '22 at 16:27
0

For the sake of conversation, lets say that every closure creates a "scope" where variables are defined - everything within the closure/scope can access variables, anything outside cannot. With that said, there are two different types of implied closures you should be aware of.

This is not an exhastive description of how closures work, but more of description of the basic practical concepts as they relate to this question. There is much more going on with how call stacks are created, lexical scoping, "global" scope within NodeJS, iframes, sandboxing, var vs const/let, block vs function scopes, and other concepts which I will leave to the reader to discover on their own.

The browser/window/global scope

In the browser, you have the top-level scope (basically window, often referred to as the "global" scope). This kind of acts as a closure in that all "non-enclosed" variables are defined as part of the window scope and are available to most other code running on that page. For example, if you include the following script in the browser, x is part of the global/window scope, whereas y and z are enclosed inside their own nested scopes.

// basic-script.js
// x becomes part of the global/window scope
const x = 10; 

(() => {
  // y is enclosed in a separate, nested closure
  const y = 20;
  // x is available in this nested scope/closure
  console.log('The sum of x and y is', x + y);

  function foo() {
    // z is enclosed in a separate, nested closure
    const z = 5;
    // x and y are both available in this nested scope/closure
    console.log('The sum of x y and z is', x + y + z);
  }

  foo();
  // z is not available to the parent scope
  console.log(z); //-> undefined
})();

// y is not available to the parent scope
console.log(y); //-> undefined
<script src="basic-script.js"></script>
<script>
   // this works because x is not enclosed
   console.log('The value of x is', x);
   // y and z are not available as they are inside a separate closure
   console.log(y, z); //-> undefined, undefined
</script>

JavaScript modules

When you write JavaScript which is imported or required by other modules, every module is automatically wrapped in its own closure. For example, if you were to import/require the above basic-script.js into another JavaScript file, the above would be automatically enclosed like this:

(function() {
  var x = 10;
  // ... other code removed for brevity
})();

There is some other magic going on for exposing exports and so forth, but that's beyond the scope of this answer (pun intended). If JS modules were not wrapped in their own closure, then you would have a mess of naming collisions and reference issues, as well as a security nightmare.

So to answer your question, yes - your second example shares a closure with "x" - the closure is implicitly created depending on your environment.

Ryan Wheale
  • 26,022
  • 8
  • 76
  • 96
0

Don't know, if you are still searching for the answer or not. What is you meant by top level, I think you are talking about the global scope. To understand closure one should understand scope better.

And No, Closure in not work in global scope. The main reason of closure is to create a function that return a function with specific variables/calculations with it so that you can reuse that value again. I have a detailed blog in scope, closure and lexical environment. You can check it out https://shimulhsn.hashnode.dev/lexical-environment-and-scope-chain

-1

A practical use of closures is creating a "scope" of protection around variables and functions declared within that function from outside interference. The top scope is "global", accessed using the window variable (an object). Everything, regardless what scope it was declared in, has access to it.

A function declared in global scope creates its own scope that no expression in the global scope and other "sub-global" scopes has access to. Functions declared in the global scope or declared inside other functions create their own scope, that even its immediate parent scope doesn't have access to.

Any variable declared—var, let, const—inside a function block is accessible in that function scope and any sub-scope created within that function scope—i.e., nothing outside that function scope can access it, (except a mechanism the function creating the scope may provide).

The second example in the question, where x is declared in the global scope, means everything has access to x. If x is later changed it is changed in all scopes. x is wide-open to interference anywhere in the code.

var x = 1; <-- declared outside func()
function func() {
  // ...etc., etc., etc.

In the first example:

function makeCounter() {
  let count = 0;

  return function() {
    return count++;
  };
}

...makeCounter() creates a scope that includes count and an anonymous function, and that has access to them. Sub-scopes within this scope also have access to them. However, any expression in the global scope or other sub-global scopes do not have access to them.


Regarding the description that closures give access to an outer function's scope from an inner function—that scope access travels "up", accessing every parent scope, all the way to global scope, regardless how far "down" the "scope-chain" it goes. For example:

const can_i_reach_global = 'yes';
topLevel();
function topLevel () {
  console.log('topLevel func, can_i_reach_global:', can_i_reach_global);
  oneDeep();
  function oneDeep () {
    console.log('oneDeep func, can_i_reach_global:', can_i_reach_global);
    twoDeep();
    function twoDeep () {
      console.log('twoDeep func, can_i_reach_global:', can_i_reach_global)
    }
  }
}

So, as far as practical definitions go, simply stating function closure "...gives you access to an outer function's scope from an inner function..." doesn't describe the usefulness of this programming construct.

bloodyKnuckles
  • 11,551
  • 3
  • 29
  • 37