DEV Community

loading...

Lexical Variable Scoping with Javascript

Kinyanjui Wangonya
👨🏾‍💻
Originally published at wangonya.com ・Updated on ・2 min read

In Javascript, code blocks are created using curly braces ({}). For example:

someFunction() {
    // some code here
}

anotherFunction() {
    // some more code here
}
Enter fullscreen mode Exit fullscreen mode

someFunction and anotherFunction are two different code blocks. These two different code blocks could also be considered as two different scopes. What that means is that the variables declared in someFunction only affect that block of code, and those declared in anotherFunction only affect that block of code. They are "scoped" in that sense. To illustrate:

var name = "Kenny"

someFunction() {
    var name = "Kyle"
    console.log("someFunction block:", name) // someFunction block: Kyle
}

anotherFunction() {
    var name = "Timmy"
    console.log("anotherFunction block:", name) // anotherFunction block: Timmy
}

console.log("global", name) // global Kenny
Enter fullscreen mode Exit fullscreen mode

As you can see, the same variable name retains its global value although it gets redefined within someFunction and anotherFunction.

Now here is where it can get a bit tricky. With that knowledge in mind, it is easy to assume that this would always be the case whenever we have code blocks. Except, it doesn't work the same with if/else statements and for loops.

var name = "Kenny";

if (name) {
  var name = "Kyle";
  console.log("if block:", name); // if block: Kyle
}

console.log("global", name); // global Kyle
Enter fullscreen mode Exit fullscreen mode

The name variable inside the if block resets the value of name. The same happens in a for loop:

var name = "Kenny";

for (var i = 0; i < 1; i++) {
  var name = "Kyle";
  console.log("for loop block:", name); // for loop block: Kyle
}

console.log("global", name); // global Kyle
Enter fullscreen mode Exit fullscreen mode

The solution is to use the let keyword instead of var.

var name = "Kenny";

if (name) {
  let name = "Kyle";
  console.log("if block:", name); // if block: Kyle
}

console.log("global", name); // global Kenny
Enter fullscreen mode Exit fullscreen mode
var name = "Kenny";

for (var i = 0; i < 1; i++) {
  let name = "Kyle";
  console.log("for loop block:", name); // for loop block: Kyle
}

console.log("global", name); // global Kenny
Enter fullscreen mode Exit fullscreen mode

Discussion (5)

Collapse
asleepysamurai profile image
Balaganesh Damodaran

Apart from explaining lexical scope, this is also a great example to show why you must never use var. var and let have different scoping rules, and as such let is always bound to the block scope, which includes conditionals and loop blocks also.

For a newbie Javascript programmer, it is much easier to understand that a variable declared with let always references the variable in the closest block scope where it is defined in.

Collapse
veevidify profile image
V

Things get crazy pretty quickly when it comes to scope and context, which is why most libraries / eslint rules enforce const usage and discourage mutating.

function createCounter() {
  let val = 0; // stateful value that lives in createCounter context
  function runCounter() {
    val ++; // mutate
    return val;
  }
  return runCounter;
}

const count = createCounter(); // count is a function that runs counter

const val1 = count(); // val1 == 1
const val2 = count(); // val2 == 2
const val3 = count(); // val3 == 3

const differentCount = createCounter(); // establish separate counter with separate state

const val4 = differentCount(); // val4 == 1
const val5 = differentCount(); // val5 == 2
Collapse
roqkabel profile image
dev-obed

Yeah.. Very helpful thanks.

Collapse
wangonya profile image
Kinyanjui Wangonya Author

I'm glad you found this helpful