In JavaScript, code in a script or a function body has a hidden object called the Lexical Environment object. The object can not be gotten from the code; It only describes how the code works.
There are two components of the lexical environment object, they are:
- Environment record object
- Reference to the outer (parent) lexical environment
Components of the lexical environment
An environment record object is an object that stores local variables as properties.
The interpretation of the examples below only exists theoretically and not practically.
See the example below:
function myName(otherName) {
const lastName = 'Bello';
const firstName = 'Osagie';
console.log(`My name is ${lastName} ${firstName} ${otherName}`);
};
myName('Noah');
The environment object of the code above is interpreted as shown below:
globalEnvironment = {
environmentRecord: {
lastName: 'Bello',
firstName: 'Osagie',
otherName: 'Noah',
},
// globalEnvironment has no outer reference
outer: null // no parent environment => discarded by JS engine
};
During the execution, there are two lexical environments, the globalEnvironment
and environmentRecord
. That is the environmentRecord
makes reference to the globalEnvironment
during execution.
Generally,
globalObject = {
...
parentObject: {
...
childObject: {
...
}
... ... ...
... ... ...
... ... ...
}
};
The global Lexical Environment has no outer reference, that’s why the
outer
isnull
.
The local variables are stored in the environmentRecord
. The parameters also are stored in the environmentRecord
. The outer
is null
because it has no parent object.
The global lexical environment is associated with the whole script.
Without the function, the code will still be within the global lexical environment.
See the example below:
const lastName = 'Bello';
const firstName = 'Osagie';
The environment object of the code above is interpreted as shown below:
globalEnvironment = {
environmentRecord: {
lastName: 'Bello',
firstName: 'Osagie',
}
outer: null
};
// globalEnvironment has no outer reference
outer: null // no parent environment => discarded by JS engine
The environmentRecord
is the parent object to lastName
and firstName
properties.
The outer
is a property within the global object, globalEnvironment
but no parent object to store its property.
The global Lexical Environment,
globalEnvironment
is for the entire script; while the environment record,environmentRecord
(internal object) is for the local variable storage. Theouter
during execution is removed from memory by the JavaScript engine to save memory since it has no parent and it is unreachable or unused.
The global Lexical Environment has no outer reference, that’s why the arrow points to null
.
The example below shows what happens when changes are made to the variables in a script.
See the example below:
// name; /* uninitialized state */
name = 'Bello'; // assigned a value
name = 'Osagie'; // changed the value
In an uninitialized state, the JavaScript engine is aware of the variable
name
but can not be referenced until it is declared withlet
.
The environment object of the code above is interpreted as shown below:
globalEnvironment = {
environmentRecord: {
// name: <uninitialized>,
name: undefined,
name: 'Bello',
name: 'Osagie'
};
outer: null
};
// globalEnvironment has no outer reference
outer: null // no parent environment
outer
is null
because there's no reference to the outer lexical environment.
In the example above initially, the variable name
was in an uninitialized state because it wasn't declared with the let
keyword. When declared with let
but not assigned a value, it became undefined
. It was then changed from name='Bello'
to name='Osagie'
making it defined.
Inner and Outer Lexical Environment
Unlike an uninitialized variable that is not ready to be used until initialized with the let
keyword, the function declaration immediately becomes ready to use from the start.
See the example below:
/*name; // uninitialized */
let name = 'John';
function greet() { // initialized
console.log(`Hello ${name}`); // Hello John
}
greet();
The environment object of the code above is interpreted as shown below:
globalEnvironment = {
environmentRecord: {
// [[Environment]]
// name: <uninitialized>,
name: 'John',
outer1: {
greet: function(name) { // initialized
console.log(`Hello ${name}`);
},
}
},
// globalEnvironment has no outer reference
outer: null // no parent environment => discarded by JS engine
};
The object outer1
above has its own hidden environment record for variables (object properties) has [[Environment]]
.
[[Environment]]
keeps the reference to the Lexical Environment where the function was created. It is available in all functions.
{
name: 'John',
environmentRecord: { greet: [Function: greet], outer: null }
}
During execution, the inner Lexical Environment is searched first, then the outer one, then the more outer one, and so on until the global one.
The two lexical environment are globalEnvironment
and environmentRecord
.
- The
environmentRecord
is the inner lexical environment that has a reference toouter1
. - The
globalEnvironment
is the outer lexical environment it holds both thename
variable and the function itself.
Let's see another example below:
function counterFunc() {
let count = 0; // from each executed makeCounter() => 1, 2
let counter = function() {
// [[Environment]]
count++; // 0 => 1, 2
return count; // 1 => 2, 3
// after each counter count, the count variable can be reached
};
return counter;
}
let makeCounter = counterFunc();
makeCounter() // 1 => count now 1, let count = 1
makeCounter() // 2 => count now 2, let count = 2
makeCounter() // 3 => count now 3, let count = 3
In the example above, at the beginning of each counterFunc()
call, a new Lexical Environment object is created to store variables for this counter()
run.
globalEnvironment = {
environmentRecord: {
count: 0, // from each executed counter.[[Environment]] => 1, 2
outer1: { // environmentRecord = outer1
counter: function() { // initialized
[[Environment]],
count++; // 0 initially, 1, 2
return count; // { count: 1 } initially, { count: 2 }, { count: 3 } ...
},
},
// globalEnvironment has no outer reference
outer: null // no parent environment => discarded by JS engine
}
};
The counter.[[Environment]]
has the reference to { count: 0 }
Lexical Environment at first execution.
Later, when counter()
is called, a new Lexical Environment is created for the call, and its outer lexical environment reference is taken from counter.[[Environment]]
.
The outer2
is cleaned up from memory by the garbage collector since it has no reference to a parent object.
counter.[[Environment]] // 1 => move to outer Lexical Environment => globalEnvironment
counter.[[Environment]] // 2 => move to outer Lexical Environment => globalEnvironment
counter.[[Environment]] // 3 => move to outer Lexical Environment => globalEnvironment
In JavaScript, all functions are naturally closures except for the
new Function
syntax.
Top comments (0)