Home > Back-end >  Does top level functions makes closure in JavaScript?
Does top level functions makes closure in JavaScript?

Time:10-29

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 ??

CodePudding user response:

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.

CodePudding user response:

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.

CodePudding user response:

Yes -- x will be part of the closure in func()

CodePudding user response:

I think of closures as 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 it's own scope that no expression in the global scope, or other "sub-global" scope, has access to. Functions declared in functions create their own scope, that even it's 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 can access it, (except a mechanism the closing function may provide).

In the second example, x is declared in the global scope, so everything has access to it. 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, that has access to count and which is return'ed. However, any expression in the global scope, or other sub-global scope not within makeCounter() does not have access to count, nor the anonymous function.


Regarding the description that closures give access to an outer function's scope from an inner function—that access reaches all the way "up" to global scope, regardless how far "down" the closure goes. For example:

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

So, as far as practical definitions go, it doesn't describe the usefulness of this programming construct.

  • Related