Skip to content

Instantly share code, notes, and snippets.

@a-laughlin
Created July 25, 2020 01:53
Show Gist options
  • Save a-laughlin/29e0c27dc071fc3340be6e7ec5043078 to your computer and use it in GitHub Desktop.
Save a-laughlin/29e0c27dc071fc3340be6e7ec5043078 to your computer and use it in GitHub Desktop.
execution context, lexical environments, variable environments

example to illustrate variable values in different scopes, in both assignment and re-assignment cases.

... html here ...
<script>
// Scope chain example, showing lexical and variable environments in a call tree.
// "this" is always window
// [name] is execution context. E.g., "win" is the window execution context.
// "[name]Vars" corresponds to [name]'s variable environment (function scoped, identifiers defined with var, and function args)
// "[name]Lexs" corresponds to [name]'s lexical environment (block scoped, identifiers defined with const and let)
// "outer" property corresponds to the closest parent environment

// assign some variables in the window execution environment
winGlobal=0; // since we're in the window execution context, including and excluding "var" keyword operate identically
var winVar=0;
let winLet=0;
const winConst=0;
// winVars:{winGlobal:0,winVar:0,foo:fn,outer:undefined},winLexs:{winLet:0,winConst:0,outer:winVars}

// foo has not yet executed
function foo(fooArg=0){

  fooGlobal=0 // fooLexs.fooGlobal missing. fooVars.fooGlobal missing. winLexs.fooGlobal missing. winVars.fooGlobal missing, but no outer to follow, so assign  winVars.fooGlobal=0.
  var fooVar=0; // defined with var, so assign to closest variable environment (fooVars)
  const fooConst=0; // defined with const, so assign to closest lexical environment (fooLexs)
  let fooLet=0; // defined with let, so assign to closest lexical environment  (fooLexs)

  // winVars:{winGlobal:0,winVar:0,fooGlobal:0,outer:undefined} winLexs:{winLet:0,winConst:0,outer:winVars}
  // fooVars:{fooArg:0,fooVar:0,outer:windowLexs} fooLexs:{fooConst:0,fooLet:0,outer:fooVars}

  // reassign. Without using keywords like let/const/var, will reassign values on closest env it's assigned
  winGlobal=1;
  winVar=1;
  winLet=1;
  winConst; // can't reassign const
  fooGlobal=1;
  fooArg=1;
  fooVar=1;
  fooLet=1;
  fooConst; // can't reassign const
  // winVars:{winGlobal:1,winVar:1,fooGlobal:1,outer:undefined} winLexs:{winLet:1,winConst:0,outer:winVars}
  // fooVars:{fooArg:1,fooVar:1,outer:winLexs}  fooLexs:{fooLet:1,fooConst:0,outer:fooVars}

  if(true){ // braces like this create a nested lexical Environment
    ifGlobal = 0;
    var ifVar = 0;
    const ifConst = 0;
    let ifLet = 0;
    // winVars:{winGlobal:1,winVar:1,foo:fn,fooGlobal:1,ifGlobal:0,outer:undefined} winLexs:{winLet:1,winConst:0,outer:winVars}
    // fooVars:{fooArg:1,fooVar:1,ifVar:0,outer:winLexs}  fooLexs:{fooLet:1,fooConst:0,outer:fooVars}
    // ifLexs: {ifLet:0,ifConst:0,outer:fooVars} (no ifVars. Block scopes (braces) only have their own lexical environment, no variable environment)

    // reassign. Without using keywords like let/const/var, will reassign values on closest env it's assigned
    winGlobal=2;
    winVar=2;
    winLet=2; 
    winConst;
    fooGlobal=2; 
    fooArg=2; 
    fooVar=2; 
    fooLet=2; 
    fooConst;
    ifGlobal = 2; 
    ifVar = 2; 
    ifLet=2; 
    ifConst;
    
    // winVars:{winGlobal:2,winVar:2,foo:fn,fooGlobal:2,ifGlobal:2,outer:undefined} winLexs:{winLet:2,winConst:0,outer:winVars}
    // fooVars:{fooArg:2,fooVar:2,ifVar:2,bar:fn,outer:winLexs}  fooLexs:{fooLet:2,fooConst:0,outer:fooVars}
    // ifLexs: {ifLet:2,ifConst:0,outer:fooVars}
    
    const bar = function(barArg=0){
      barGlobal = 0;
      var barVar = 0;
      const barConst = 0;
      let barLet = 0;
      // winVars:{winGlobal:2,winVar:2,foo:fn,fooGlobal:2,ifGlobal:2,barGlobal:0,outer:undefined} winLexs:{winLet:2,winConst:0,outer:winVars}
      // fooVars:{fooArg:2,fooVar:2,ifVar:2,bar:fn,outer:winLexs}  fooLexs:{fooLet:2,fooConst:0,outer:fooVars}
      // ifLexs: {ifLet:2,ifConst:0,bar:fn,outer:fooVars}
      // barVars:{barArg:0,barVar:0,outer:ifLexs}  barLexs:{barLet:0,barConst:0,outer:barVars}
      
      // reassign. Without using keywords like let/const/var, will reassign values on closest env it's assigned
      winGlobal=3;
      winVar=3;
      winLet=3; 
      winConst;
      fooGlobal=3; 
      fooArg=3; 
      fooVar=3; 
      fooLet=3; 
      fooConst;
      ifGlobal=3; 
      ifVar=3; 
      ifLet=3; 
      ifConst;
      barGlobal=3; 
      barArg=3; 
      barVar=3; 
      barLet=3; // reassign wherever it's most closely assigned
      barConst; // can't reassign const

      // bar() has executed
      // winVars:{winGlobal:3,winVar:3,foo:fn,fooGlobal:3,ifGlobal:3,barGlobal:3,outer:undefined} winLexs:{winLet:3,winConst:0,outer:winVars}
      // fooVars:{fooArg:3,fooVar:3,ifVar:3,bar:fn,outer:winLexs}  fooLexs:{fooLet:3,fooConst:0,outer:fooVars}
      // ifLexs: {ifLet:3,ifConst:0,bar:fn,outer:fooVars} (no ifVars. Block scopes (braces) only have their own lexical environment, no variable environment)
      // barVars:{barArg:3,barVar:3,outer:ifLexs}  barLexs:{barLet:3,barConst:0,outer:barVars}
    }
    bar();
  }
  // bar() and foo() have executed
  // winVars:{winGlobal:3,winVar:3,foo:fn,fooGlobal:3,ifGlobal:3,barGlobal:3,outer:undefined} winLexs:{winLet:3,winConst:0,outer:winVars}
  // fooVars:{fooArg:3,fooVar:3,ifVar:3,bar:fn,outer:winLexs}  fooLexs:{fooLet:3,fooConst:0,outer:fooVars}
  // ifLexs: undefined since we're outside the if(true){} block scope
  // barVars: undefined since we're outside bar's function scope
}
foo();
// bar() and foo() have executed
// winVars:{winGlobal:3,winVar:3,foo:fn,fooGlobal:3,ifGlobal:3,barGlobal:3,outer:undefined} winLexs:{winLet:3,winConst:0,outer:winVars}
// fooVars/fooLexs: undefined since we're outside foo's function scope
// ifLexs: undefined since we're outside the if(true){} block scope
// barVars/barLexs: undefined since we're outside bar's function scope
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment