64

In JavaScript, var declarations create properties on the global object:

var x = 15;
console.log(window.x); // logs 15 in browser
console.log(global.x); // logs 15 in Node.js

ES6 introduces lexical scoping with let declarations that have block scope.

let x = 15;
{
   let x = 14;
}
console.log(x); // logs 15;

However, do these declarations create properties on the global object?

let x = 15;
// what is this supposed to log in the browser according to ES6?
console.log(window.x); // 15 in Firefox
console.log(global.x); // undefined in Node.js with flag
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504

5 Answers5

66

Do let statements create properties on the global object?

According to the spec, no:

A global environment record is logically a single record but it is specified as a composite encapsulating an object environment record and a declarative environment record. The object environment record has as its base object the global object of the associated Realm. This global object is the value returned by the global environment record’s GetThisBinding concrete method. The object environment record component of a global environment record contains the bindings for all built-in globals (clause 18) and all bindings introduced by a FunctionDeclaration, GeneratorDeclaration, or VariableStatement contained in global code. The bindings for all other ECMAScript declarations in global code are contained in the declarative environment record component of the global environment record.

Some more explanation:

  • A declarative environment record stores the bindings in an internal data structure. It's impossible to get a hold of that data structure in any way (think about function scope).

  • An object environment record uses an actual JS object as data structure. Every property of the object becomes a binding and vice versa. The global environment has an object environment object whose "binding object" is the global object. Another example is with.

Now, as the cited part states, only FunctionDeclarations, GeneratorDeclarations, and VariableStatements create bindings in the global environment's object environment record. I.e. only this bindings become properties of the global object.

All other declarations (e.g. const and let) are stored in the global environment's declarative environment record, which is not based on the global object.

t.niese
  • 39,256
  • 9
  • 74
  • 101
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 1
    But 8.1.1.1 (https://people.mozilla.org/~jorendorff/es6-draft.html#sec-declarative-environment-records) indicates that `let` and `var` create environment records of the same type (i.e. they both create `declarative environment records`), suggesting that `let` and `var` positions on the global object might correspond. I suppose, the global case might be special, however. – Ben Aston Feb 28 '15 at 00:03
  • 1
    `let` and `var` don't create records. `let` and `var` bindings **are "stored" in** environment records. And there are different types of environment records: declarative (what you linked to), object, function, module and global, where declarative and object records are the two primary types (and all others are derivates). – Felix Kling Feb 28 '15 at 00:06
  • Please can you clarify the purpose of your making the distinction. If I declare a variable in the global scope, an environment record is created (and the variable is stored within it like you say). So we are both "correct" no? Are you saying the type of environment record used depends on whether we are in the global scope or not? – Ben Aston Feb 28 '15 at 00:08
  • Felix, can you confirm that `VariableStatement` (mentioned in your quote) means specifically function-scoped declaration, or does it include `let` too? I presume the former. – Ben Aston Feb 28 '15 at 00:12
  • Here is the definition of *VariableStatement*: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-variable-statement. It has nothing to do with functions, it only describes the `var....` syntax. – Felix Kling Feb 28 '15 at 00:13
  • OK, based on this discussion I have to agree. +1 – Ben Aston Feb 28 '15 at 00:14
  • ...It does have something to do with functions in as much as var is function-scoped. Nothing more. – Ben Aston Feb 28 '15 at 00:15
  • @BenAston: I can have `var` statements outside of functions though (global scope, module scope), so that statement is not 100% correct. – Felix Kling Feb 28 '15 at 00:16
  • 9
    To clarify some things: The big difference between a declarative record and an object record is that a declarative record uses an internal data structure to store the bindings. An object record uses an actual JS object as "storage". In ES5, the global environment was defined as object record (http://es5.github.io/#x10.2.3), and that's why all variable declarations in global scope become properties of the global object. In ES6 though, the global environment consist of two different environment record types, and only `var` declarations are stored in the object record, `let` declarations are not. – Felix Kling Feb 28 '15 at 00:18
  • (as per the statement I quoted). – Felix Kling Feb 28 '15 at 00:19
  • Spot on as usual - thanks Felix. You might want to make this answer more approachable to people who are less acquainted with the terminology but it's good as it is. – Benjamin Gruenbaum Feb 28 '15 at 01:05
  • @Benjamin: I tried with the additional explanation. I might add more later... Thanks for asking interesting questions :) – Felix Kling Feb 28 '15 at 01:10
  • How will i mark global variables declared with `let` for garbage collection from another script file? – Beingnin Jun 13 '19 at 08:01
  • @NithinChandran: If you can always reference any global variable by just its name. E.g. `foo = null`. – Felix Kling Jun 13 '19 at 08:09
  • @FelixKling but in my case the global variable(using `let`) is declared in one script file and making it null from another script file. It is showing that the variable is not defined while trying to assign null – Beingnin Jun 13 '19 at 08:15
  • @NithinChandran All scripts share the same global scope. If you get an error that means that either variable doesn’t exist yet, or it hasn’t been initialized yet or it is actually not global. – Felix Kling Jun 13 '19 at 08:22
  • @FelixKling but it is perfectly working when declared using `var` – Beingnin Jun 13 '19 at 12:03
  • @NithinChandran: then you are likely trying to assign null to the variable before it was actually initialized. E.g `function clear() { foo = null;}; clear(); var foo = 43;` works but changing `var` to `let` will throw. – Felix Kling Jun 13 '19 at 18:12
7

Standard scripts:

Both let and var variables, if declared at the top-level of a script, are accessible outside of the script file. However, only var variables get assigned to the window object. Have a look at this code snippet as proof:

<script>
  var namedWithVar = "with var";
  let namedWithLet = "with let";
</script>

<script>
  console.log("Accessed directly:");
  console.log(namedWithVar);        // prints: with var
  console.log(namedWithLet);        // prints: with let
  console.log("");

  console.log("Accessed through window:");
  console.log(window.namedWithVar); // prints: with var
  console.log(window.namedWithLet); // prints: undefined
</script>

Javascipt modules:

Note that modules are a different story. Variables declared in a module are not made available in the global scope:

<script type="module">
  var namedWithVar = "with var";
  let namedWithLet = "with let";
</script>

<script>
  console.log(namedWithVar); // ReferenceError
</script>

<script>
  console.log(namedWithLet); // ReferenceError
</script>

<script>
  console.log(window.namedWithVar); // prints: undefined
  console.log(window.namedWithLet); // prints: undefined
</script>
Flimm
  • 136,138
  • 45
  • 251
  • 267
6

Per the specification:

"let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment."

This means that you should be able to access the variable inside the execution scope, but not outside. This expands the execution scope beyond the classic JS closure structure of function-only or global.

Defining a let variable globally should not expose the variable on the global context, as used to be the case in Firefox. In practice you should not define variables in a global context.

Tracker1
  • 19,103
  • 12
  • 80
  • 106
  • 3
    *"Defining a let variable globally leaves this open to interpretation"* As you can see from my answer, it does not. What you say is true, we really do have more things that create scope (modules as well), however, that alone does not answer the posed question. – Felix Kling Feb 28 '15 at 06:17
  • 1
    Firefox appears to have changed it's behavior to be in-line with V8, no longer binding let and const declarations to the global scope. – ecc521 Jul 07 '19 at 21:17
1

Variables declared via let keyword do not create accessible properties on a global object (window for a browser). Actually, Firefox fixed its behavior: let v = 42; 'v' in window // false

daGo
  • 2,584
  • 26
  • 24
0

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:

   var x = 'global';
   let y = 'global';
   console.log(this.x); // "global"
   console.log(this.y); // undefined

The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global. If you re-declare a JavaScript variable, it will not lose its value. For example:

var x = 1;

if (x === 1) {
  var x = 2;

  console.log(x);
  // output: 2
}

console.log(x);
// output: 2

Note: that unlike C, C++, and Java, JavaScript does not have block-level scope when you declare a variable using var.

As we mentioned before let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. For example:

let x = 1;

if (x === 1) {
  let x = 2;

  console.log(x);
  // output: 2
}

console.log(x);
// output: 1

Here I recommend you to read about Variable Scope

S. Mayol
  • 2,565
  • 2
  • 27
  • 34