13

I need to understand the difference between Variable Environment vs lexical environment in JavaScript. Actually I go through some notes available in stackoverflow and the doc but they are very hard to understand.I would be happy if you can explain it to me simply due my bad English knowledge

Thomson
  • 20,586
  • 28
  • 90
  • 134
user3601733
  • 189
  • 1
  • 1
  • 9
  • Check [this](http://stackoverflow.com/questions/1047454/what-is-lexical-scope?rq=1) and [this](http://stackoverflow.com/questions/500431/javascript-variable-scope?rq=1) and post only very specific questions. – thefourtheye May 30 '14 at 06:16
  • Of course there is always [*ECMA-262*](http://ecma-international.org/ecma-262/5.1/#sec-10.3). – RobG May 30 '14 at 06:19
  • @thefourtheye—is *scope* == *environment* here? – RobG May 30 '14 at 06:21
  • @RobG I thought he really meant scope... – thefourtheye May 30 '14 at 06:22
  • http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/ i found the variable environment fro this link but i couldnot understand it .can u help me to undarstand this . – user3601733 May 30 '14 at 06:32

3 Answers3

15

Note

This answer relates to ECMA-262 ed 5.1. Later editions have modified the description of variable and lexical environments to accommodate lexical scoping of let and const (which are both block scoped).

According to ECMA-262 §10.3, variable environment is a certain type of lexical environment. Both are "specification types" that are used solely to describe features of ECMAScript. You can't access them or modify them directly in anyway, and ECMAScript implementations don't have to implement them in any specific way, they just have to behave as if they were implemented per the specification.

A lexical environment consists of an environment record, which can be thought of as an object whose properties are the variable and function names declared within the associated execution context. It also has, for functions, identifiers from the formal parameter list in the function declaration or expression (e.g. function foo(a, b){} effectively declares a and b as variables on foo's environment record).

A lexical environment also has a link to any outer lexical environment (i.e. its scope chain), so it is used to resolve identifiers outside the current an execution context (e.g. global variables from within a function). They can be associated with other structures besides functions and execution contexts, e.g. try..catch and with statements.

A variable environment is just the part of a lexical environment within the execution context, essentially just the variables and functions declared within the current context.

Anyone who wants to correct the above, please chime in.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • so what do u mean by exection context – user3601733 May 30 '14 at 07:08
  • Execution context is described in [ECMA-262](http://ecma-international.org/ecma-262/5.1/#sec-10.4), it's also called "current scope". There are global, function and eval contexts. Think of it as the entire environment that a variable declaration is scoped within. It includes all the local variables, function declarations, parameters, scope chain and *this*. – RobG May 30 '14 at 09:32
  • So the difference between variable environment and environment record in the same execution context is that the latter contains formal parameter names? – Thomson Nov 11 '16 at 09:26
  • An [*environment record*](http://ecma-international.org/ecma-262/7.0/index.html#sec-environment-records) is effectively the term introduced in ECMAScript 2016 for [*variable environment*](http://ecma-international.org/ecma-262/5.1/#sec-10.3), which was used in ES5 and earlier versions of the specification. – RobG Nov 11 '16 at 20:28
  • 3
    As of ES6 it's not true in general that the variable environment contains "the variables and functions". It only contains variables declared with `var`, as `let` and `const` use the lexical environment. – Trevor Merrifield Aug 21 '17 at 00:57
  • @RobG also I think that most recent comment is incorrect. A LexicalEnvironment and VariableEnvironment both are of type LexicalEnvironment, and every LexicalEnvironment (the type) contains an environment record (an abstract type). See http://ecma-international.org/ecma-262/6.0/#sec-lexical-environments – Trevor Merrifield Aug 21 '17 at 01:04
  • @TrevorMerrifield—this answer is from 2014, so relates to ECMA-262 ed 5.1 (aka "ES5"), not ECMAScript 2015 (aka "ES6"). *let* and *const* didn't exist then, so when introduced required modification of specification types. – RobG Aug 21 '17 at 04:57
  • 1
    @RobG understood, my first comment is only meant to tip off any googlers that things have changed :) – Trevor Merrifield Aug 21 '17 at 05:09
  • However this doesn't affect my second comment: the roles of LexicalEnvironment and VariableEnvironment didn't entirely change meaning from 5.1 to to 6.0. You can see the relevant portion of the docs are the same in 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-10.2 – Trevor Merrifield Aug 21 '17 at 05:11
  • Yeah.. but in reality variable environment just doesn't make sense and it isn't implemented in the V8 engine. the idea of lexical environment is much closer to reality. every function have a lexical environment that contain all `var` `let` and `const` not in a separated way they are all part of the same memory space. i believe variable environment just meant to help us understand that when we declare variable of type `var` inside a block it wont be part of that block but rather part of the variable environment so it's "function scoped". but in reality it's just the parent lexical environment – Nadav Shlush Sep 28 '21 at 10:43
  • @NadavShlush—they're all just specification devices to provide a grammar to discuss running code. There – RobG Sep 28 '21 at 14:29
14

LexicalEnvironment and VariableEnvironment are what keep track of variables during runtime and correspond to block scope and function/module/global scope respectively. Here's an example based on my reading of the spec http://www.ecma-international.org/ecma-262/6.0/

0:  function do_something() {
1:     var a = 1;
2:     let b = 2;
3:     while (true) {
4:         var c = 3;
5:         let d = 4;
6:         console.log(b);
7:         break;
8:     }
9:  }
10:
11: do_something();

When we first call do_something() it creates an ExecutionContext.

ExecutionContext:
    LexicalEnvironment:
        b -> nothing
        outer: VariableEnvironment //here should VariableEnvironment
    VariableEnvironment:
        a -> undefined, c -> undefined
        outer: global
    ...

Entering the while loop creates a new lexical environment:

ExecutionContext:
    LexicalEnvironment:
        d -> nothing
        outer:
            LexicalEnvironment
                b -> 2
                outer: global
    VariableEnvironment:
        a -> 1, c -> undefined
        outer: global
    ...

Now when we look up variables, we can always fall back on whatever is contained in outer. This is why you can access global variables from within functions. It is also why we can access console.log(b) from within the while block even though it lives in the outer scope.

When we leave the while block we restore the original lexical environment.

ExecutionContext:
    LexicalEnvironment
        b -> 2
        outer: global
    VariableEnvironment:
        a -> 1, c -> 3
        outer: global

Therefore d is no longer accessible.

Then when we leave the function we destroy the execution context.

I think that's the gist of it.

Although this explanation is based on ECMA-262 6.0 because of let, the LexicalEnvironment is defined similarly in 5.1 where it's used to temporarily bind variables in with, and in the catch clause of a try/catch.

Joiner
  • 81
  • 9
Trevor Merrifield
  • 4,541
  • 2
  • 21
  • 24
  • 1
    Good explanation, but it seems and error from `while` loop block. Latest `outer` block should point out to `VariableEnvironment` and it should point out to `global`. Am I right? – Dracontis Jan 18 '19 at 08:04
  • @Dracontis, also think so, otherwise it seems that there's no way we can get the value of **_a_** from inside the while loop. – Ledorub Oct 01 '21 at 22:58
3

It's easier to understand the Lexical Environment here when you break it up into two more underlying concepts, the Variable Environment and the Outer Environment.

Each Execution Context has an Outer Environment and a Variable Environment. The Outer Environment and the Variable Environment make up the Lexical Environment. That is, the Variable Environment is a certain type of Lexical Environment. A Lexical Environment can be thought of as an internal JavaScript engine construct that holds identifier-variable mappings. The identifier is the name of the variable or function and variable is a reference to the actual data type stored by the identifier (e.g. Object, Number, String, Boolean, null, undefined). A Lexical Environment also has a link to any Outer Environment (i.e. its scope chain), so it is used to resolve identifiers outside the current Execution Context. Ultimately, a corresponding Lexical Environment is created for every Execution Context.

In simplest terms, Variable Environment refers to where the variables live that you created. In the below example, each myVar is distinct and they do not touch each other. First, a global Execution Context is created. In the Creation Phase of the Execution Context, both the Outer Environment and Variable Environment of the Lexical Environment is created. In terms of the Variable Environment, myVar is placed in memory with undefined value and b and a functions are put into memory with a reference to their definitions. These properties are attached to the global object referenced by 'this'. The properties we defined in the global Execution Context are stored in the global Variable Environment. Then in the Execution Phase of the Execution Context, myVar is assigned the value of 1. So now in memory for the global Execution Context, myVar has a value of 1. This is stored in the global Variable Environment.

Then during execution, the a function is invoked. A new Execution Context is created and put on the Execution Stack. It goes through the Creation and Execution Phase of the Execution Context. The myVar variable declared here is placed in a new area in memory separate from the other Execution Context. A Lexical Environment is created mapping the identifier to its variable. It in effect creates a Variable Environment for any variables defined in this Execution Context. This Variable Environment is distinct from any other Variable Environment. The Execution Phase occurs and myVar is assigned a value of 2. Now in this Variable Enviroment, myVar has a value of 2, whereas in the global ExecutionContext, myVar has a value of 1. Note that if we were to refer to a variable in this new Execution Context that does not exist in the current Execution Context, then the Lexical Environment will search its parent Lexical Environment for the variable, aka the Outer Environment. Since JavaScript is single-threaded, once myVar is assigned a value of 2, it moves on to the next statement, which is the invocation of b, and a new Execution Context is created for b and the same process occurs again.

function b(){
  var myVar;
  console.log(myVar);
}

function a(){
  var myVar = 2;
  console.log(myVar);
  b();
}

var myVar = 1;
console.log(myVar);
a();
console.log(myVar);

> 1
> 2
> undefined
> 1

Again, it's important to emphasize that each of the myVars live in their own Variable Enviroment respective to their own Execution Context. Hence, when we console.log(myVar) after calling the a function, myVar in the global Execution Context is still the value of 1. In fact, when we do the second console.log(myVar), both the a and b function's Execution Context will already be popped.

It's also very important to note here that since these functions are invoked without the new keyword, this refers to the object in the global Execution Context. This can be proven easily:

var a = 1;

function b(){
    var a = 2;
    console.log(this.a);
}

b()
> 1

Above, this.a refers to the a defined in the global Execution Context, since this is refering to the global object, which in browsers is the Window object.

Now that we discussed the Variable Environment, let's discuss the Outer Enviroment of the Lexical Environment. This leads us to the Scope Chain. First, we must ask what is an Outer Environment of an Execution Context? In the below example, in the case of the function b, its Outer Environment is the Global Execution Context. This is also the case of the function a. This is true for function b, even though function a is directly below function b in the Execution Stack. The Outer Environment invokes the concept of the Lexical Environment. The Lexical Environment highlights the idea that where something is written physically in your code is important. It determines how identifier/variable mappings live in your code and how they will connect to each other. So we must ask ourselves, where does function b sit lexically? It lexically sits on top of the global Outer Environment. The function b is not sitting inside of function a; instead, it is sitting at the same level as the global Outer Environment. When you ask for a variable while running a line of code within any particular Execution Context, if the Engine cannot find the variable within the Variable Environment of the current Execution Context, it will look for the variables in the Outer Environment of the current Execution Context. Now even if there are 10 Execution Contexts' stacked on each other, if the 10th Execution Context is sitting lexically on the global Outer Environment, when it searches for its Outer Environment in each of the other 9 Execution Contexts', it will search all the way to the global Execution Context, since the code sits lexically on the Outer Environment. This process of searching for a given Execution Context's Outer Environment is known as the Scope Chain in JavaScript. Remember scope asks 'where can I access a variable?'. And the Scope Chain is those links of Outer Environment references.

function b(){
  console.log(myVar);
}

function a(){
  var myVar = 2;
  b();
}

var myVar = 1;
a();
> 1

You might have thought that myVar in b would be 2, since the b function would look into its parent Execution Context, which is a (which is the Execution Context directly below b's Execution Context in the Execution Stack). But that is not how the Outer Environment of the Lexical Environment works. Because the Outer Environment of b is the global Execution Context, myVar in b will be the value 1.

Now it's possible to change the Lexical Environment of a function. We can change the lexical environment of the function b by placing it physically inside of function a. Since we changed its physical location, the Outer Environment of the Lexical Environment of the function b changes.

Daniel Viglione
  • 8,014
  • 9
  • 67
  • 101