When inspecting scopes of a function in the DevTools console I noticed a "script" scope. After a bit of research it seems to be created for let
and const
variables.
Scopes of a function in a script without const
or let
variables:
Scopes of a function in a script with a let
variable:
Yet the following prints 1
in the console - variables in the script scope can still be accessed from other scripts:
<script>let v = 1</script>
<script>console.log(v)</script>
I've heard of ES6 modules in which top-level variables won't be accessible from outside a module. Is that what the scope is used for or does it have any another purpose?
Script scope is the nearest ancestor script file's scope or Global if there is no nearest ancestor script file. using: - Used to access variables defined in another scope while running scripts via cmdlets like Start-Job and Invoke-Command .
In JavaScript, objects and functions are also variables. Scope determines the accessibility of variables, objects, and functions from different parts of the code.
An important idea in programming is scope. Scope defines where variables can be accessed or referenced. While some variables can be accessed from anywhere within a program, other variables may on… Before we talk more about scope, we first need to talk about blocks.
A scope is a region of the program and broadly speaking there are three places, where variables can be declared: Inside a function or a block which is called local variables, In the definition of function parameters which is called formal parameters. Outside of all functions which is called global variables.
JavaScript doesn't have "script scope."¹ What you're seeing there is just what Google's V8 JavaScript engine calls the part of the global environment that holds the new style of lexically-scoped globals created when you use let
, const
, and class
at global scope. They're still globals, but they're different from the older style of globals created by var
and function declarations at global scope (which V8 shows in Global
under [[Scopes]]
). The V8 debugger lists the two types of globals in those two different places.
You can stop reading here if you like, but if you want the nitty-gritty details, read on. :-)
So why are there two global parts to the global environment? In a word: History.
JavaScript's original form of globals (global var-scoped bindings²), had multiple issues. The main two were:
this
at global scope, also accessible via the window
global on browsers or the newer globalThis
global defined by the spec). That meant you could look in the global object to find things that you didn't know the name of (by using for-in
, Object.keys
, or similar).Aside from those issues at global scope, var
had the issue that it didn't have block scope; and function declarations (which also create var-scoped bindings) in blocks were unspecified but allowed as an extension, resulting in largely incompatible semantics for them across JavaScript implementations.
When it came time to add a new way of declaring things with better semantics (let
, const
, class
; "lexically-scoped bindings"), the committee that moves JavaScript forward (ECMA TC39) had to figure out how those new semantics would work at global scope. Their solution was to have two parts to the global environment — one for the old style, and other for the new style — but still treat it "logically" as a single environment. From the specification:
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.
An "environment record" is a conceptual object that holds bindings² (variables and such) and some other things. Joining that up with what you're seeing in your screenshot:
Global
under [[Scopes]]
.Script
under [[Scopes]]
.In your screenshot, you have let f
, which creates a lexically-scoped binding called "f"
, so V8 shows that under [[Scopes]].Script
. If you had var f
instead, V8 would show that under [[Scopes]].Global
. But again, both are globals.
What does it mean when they say the two parts of the global environment are "logically" a single record? Basically they mean that it's not just two nested environments (although in many ways it behaves like it is), there is only one global scope (even though there are two parts to the environment related to it). One way you can see that is that you can't declare something with both var
and let
at global scope, it's an error:
var a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
If they were just nested environments, you'd be allowed to do that — but how confusing that would be!
But while they aren't just nested, they are nested. You can prove that by creating the var-scoped global without using var
(by assigning to a property on the global object):
window.a = "var-scoped a";
let a = "lexically-scoped a";
console.log(a); // "lexically-scoped a"
console.log(window.a); // "var-scoped a"
let b = "lexically-scoped b";
window.b = "var-scoped b";
console.log(b); // "lexically-scoped b"
console.log(window.b); // "var-scoped b"
It perhaps goes without saying that you shouldn't do that on purpose, but it demonstrates the nesting aspect of the dual environment.
¹ It does have module scope, which is different, but the top-level code in non-module scripts like yours are executed at global scope.
² A binding is the combination of a name (like a
) and a storage slot for its current value. Variables are bindings. So are constants, parameters, the variable created by a function declaration, and various built-in things like this
.
When you declare a variable using var
on the top level (i.e. not inside a function), it automatically becomes a global variable (so in browser you can access it as a property of window
). It's different with variables declared using let
and const
—they don't become global variables. You can access them in another script tag, but you can't access them as properties of window
.
See this example:
<script>
var test1 = 42;
let test2 = 43;
</script>
<script>
console.log(test1); // 42
console.log(window.test1); // 42
console.log(test2); // 43
console.log(window.test2); // undefined
</script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With