JavaScript Scopes
Planted:
Status: seed
POLP (The Principle of Least Privilege) states components of a system should be designed to function with least privilege, access & exposure. This makes the overall system stronger from a security standpoint. A compromise or failure of 1 piece has a minimized impact on the rest of the system. Other benefits include avoiding naming collisions, unexpected behaviour & unintended dependencies. For each piece, default to exposing the minimum. Keep everything else private. A scope enables control of a declaration's exposure.

Levels
In JavaScript, scopes are determined at compile time. This is known as lexical scope. Each scope can only access declaration within itself or in parent scopes. However, during compliation, declarations may move to a different scope. There are 4 levels of scope:
/index.js
function diff(x,y) { if (x > y) { let tmp = x; x = y; y = tmp; } return y - x; } const x = 2; const y = 4; diff(x, y);
If a .js
file is being imported into a .html
file, its outer most scope is the global scope. Although the function arguments are highlighted, they aren't included in the global scope. Details below.
Arguments
Function & block scopes are between the {}
brackets.
This means a function's arguments aren't within a function's scope.
They aren't in a function's parent scope either.
When compiled, the location of arguments can be thought of as being in a new scope that wraps the function.
Before Compile
/index.js
function myFunc(input) { return input } myFunc(1)
After Compile
/index.js
{ var input = 1 function myFunc() { return input } } myFunc(1)
Before Compile
/index.js
const myArray = [...] for (let i = 0; i < myArray.length; i++) { ... } ...
After Compile
/index.js
const myArray = [...] { let i = 0 for (; i < myArray.length; i++) { ... } } ...
This is why const
can't be used in a for
loop.
It needs to be re-assigned after the 1st iteration via i++
.
Accessing Variables & Functions
When a reference cannot be found within a scope, the parent scope is searched.
If it still can't be found, the next parent scope is searched.
This continues until the reference is found or there are no more scopes to search.
At that point, a reference error will be thrown.
Below is an example of a variable that is declared in the global scope but accessed from a function scope.
The engine 1st searches the function scope for myVar
.
It can't find it, so it then searches the parent scope & finds the declaration.
/index.js
Console
const myVar = 1 function myFunc() { console.log(myVar) } myFunc()
Global Scope
The global scope is where:
- ▪ JavaScript exposes:
- ▫ primitives
- ▫ natives
- ▫ global functions:
eval()
,parseInt()
, ... - ▫ namespaces:
Math
,JSON
- ▫ Intl, WebAssembly
- ▪ The environment hosting the JS engine exposes its own built-ins:
- ▫
console
- ▫ the DOM (
window
,document
, ...) - ▫ timers (
setTimeout
(..), etc) - ▫ web platform APIs: navigator, history, geolocation, WebRTC, etc.
- ▫
If the environment is the browser.
A var
or function
declaration in the global scope will be added to the global object.
This object is commonly accessed through window
.
However, it is better to use the standardized reference globalThis
instead.
/index.js
Console
var myVar = 1; console.log(globalThis.myVar);
IIFE
An application could include many global variables & functions from different source files. Limiting the number of these prevents problems like name collisions. 1 way to do this is to use an IIFE (Immediately Invoked Function Expression). Commonly used for initiating code (code that runs as soon as the JavaScript loads), it is a function that:
- ▪ is invoked as soon as it is defined,
- ▪ doesn't pollute the global namespace &
- ▪ can't be invoked again.
index.js
(function () { // ... })();