JavaScript Lexical Environment
Tags: webdev, tutorial, javascript, codenewbie
Published: 2021-04-03 18:23:53

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:

```js
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:

```js
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` is `null`.

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:

```js
const lastName = 'Bello';
const firstName = 'Osagie';
```

The environment object of the code above is interpreted as shown below:

```js
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. The `outer` 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:

```js
// 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 with `let`.

The environment object of the code above is interpreted as shown below:

```js
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:

```js
/*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:

```js
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.

```js
{
  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 to `outer1`.
- The `globalEnvironment` is the outer lexical environment it holds both the `name` variable and the function itself.

Let's see another example below:

```js
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.

```js
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.

```js
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](https://techstack.hashnode.dev/javascript-function-closures) except for the `new Function` syntax. 

---
[![Buy me a Coffee](https://res.cloudinary.com/bizstak/image/upload/v1610317510/ko-fi-trans_fr3su4.png)](https://www.buymeacoffee.com/techstackmedia)

---