What Is Variable Shadowing in JavaScript?

By definition, an outer scoped variable is said to be "shadowed" by an inner scoped variable when both of them have the same name.

To understand variable shadowing in JavaScript, you must first be aware of the two scopes you can declare variables in:

  1. Local Scope: Variables declared here are block-scoped or function-scoped;
  2. Global Scope: It's the outermost scope where any variable that's declared is available globally.

And, the three ways in which you can declare variables in those two scopes are as follows:

  1. Using var — creates a function-scoped or globally-scoped variable;
  2. Using let — creates a block-scoped local variable;
  3. Using const — creates a block-scoped local constant.

Now that you know the basics, you must remember the following rules that apply to variables depending on the scope they're declared in:

  1. Variable Type and Scope Determine if the Value of Shadowed Variable Is Overwritten;
  2. Local Scope Variables Take Precedence;
  3. let or const Cannot be Shadowed in a Local Scope.

Variable Type and Scope Determine if the Value of Shadowed Variable Is Overwritten

Variables declared as let or const are block-scoped and they do not overwrite the value of the shadowed variable. For example:

// global scope
var foo = 'bar';

if (true) {
    // block-scoped (local)
    let foo = 'qux';
    console.log(foo); // 'qux'
}

console.log(foo); // 'bar'

This isn't true, however, for variables declared with var since they are not block-scoped (but rather global and function scoped). Therefore, they would overwrite values in block scope but not in function scope. For example:

// global scope
var foo = 'bar';

if (true) {
    // block-scoped (local)
    var foo = 'qux';
    console.log(foo); // 'qux'
}

console.log(foo); // 'qux'

Since variables declared as var are function-scoped, the nested function in the following example won't overwrite the one with the same name in the outer scope:

function printFoo() {
    var foo = 'bar';

    function printQux() {
        var foo = 'qux';
        return foo;
    }

    console.log(printQux()); // 'qux'
    console.log(foo); // 'bar'
}

printFoo();

Local Scope Variables Take Precedence

When you use a local scoped variable of the same name, its value takes precedence over the outer scoped one. For example:

// global scope
var foo = 'bar';

if (true) {
    // block-scoped (local)
    const foo = 'qux';
    console.log(foo); // 'qux'
}

console.log(foo); // 'bar'

Same thing happens in a function scope as you can see in the code below:

// global scope
var foo = 'bar';

function printFoo() {
    // function scope (local)
    const foo = 'qux';
    // ...
    return foo;
}

console.log(printFoo()); // 'qux'
console.log(foo); // 'bar'

let or const Cannot be Shadowed in a Local Scope

In a local scope you cannot shadow another let or const. In that case, you will get an error as shown below:

function printFoo() {
    let foo = 'bar';

    if (true) {
        // SyntaxError: Identifier 'foo' has already been declared
        var foo = 'qux';
    }
    // ...
    return foo;
}

However, a variable declared with let, const and var can shadow a global-scoped let, const and var variable. For example:

// global scope
const foo = 'bar';

if (true) {
    // block-scoped (local)
    const foo = 'qux';
    console.log(foo); // 'qux'
}

console.log(foo); // 'bar'

Conclusion

Variable shadowing can easily lead to confusion and unintended results, and consequently unwanted bugs in your code. To illustrate this, let's consider as an example, the following where the variable "name" is shadowed:

let name = 'John';

['Jane', 'Wayne', 'Bruce'].forEach((name) => {
    console.log(name); // 'Jane' 'Wayne' 'Bruce'
});

console.log(name); // 'John'

Or, a simple for loop, where the variable "i" is shadowed:

let i = 10;

for (let i = 0; i < 3; i++) {
  console.log(i); // 0 1 2
}

console.log(i); // 10

As you can see, code like that can easily cause confusion or mistakes. Therefore, it is best to avoid it. You could, perhaps, do the following to remedy variable shadowing:

  • Write and organize your code better, such as by employing best engineering principles (e.g. SOLID, etc.) and modularizing your code;
  • Name your variables in a descriptive way, such that it prevents you from having duplicate variables names in the same module.

This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.