DEV Community

Muhammad Ahmad
Muhammad Ahmad

Posted on

Variable Shadowing in JavaScript

When two outer scoped variables have the same name, they are said to be "shadowed" by an inner scoped variable.

To understand variable shadowing in JavaScript, you must first understand the two scopes in which variables may be declared:

  1. Local Scope: Variables declared here are either block-scoped or function-scoped.
  2. Global Scope: Any variable declared here is available globally.

The three methods for declaring variables in the two scopes are as follows:

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

Now that you've mastered the fundamentals, keep in mind the following rules, which apply to variables based on the scope in which they're defined:

  1. Variable Type and Scope Determine If the Value of Shadowed;
  2. Variable Is Overwritten;
  3. Local Scope Variables Take Precedence;

let or const cannot be shadowed in a Local Scope.

If the value of a shadowed variable is overwritten, it is determined by the type and scope of the variable.

let and const variables are block-scoped, meaning they don't overwrite the value of the shadowed variable. Consider the following scenario:

// global scope
var foo = 'bar';

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

console.log(foo); // 'bar'
Enter fullscreen mode Exit fullscreen mode

However, because variables defined using var are not block-scoped, this is not the case (but rather global and function scoped). As a result, values in block scope would be overwritten, but not in function scope. Consider the following scenario:

// global scope
var foo = 'bar';

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

console.log(foo); // 'qux'
Enter fullscreen mode Exit fullscreen mode

Because var variables are function-scoped, the nested function in the following example will not replace the outer scope function with the same name:

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

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

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

printFoo();
Enter fullscreen mode Exit fullscreen mode

Local Scope Variables Take Precedence

When you use a local scoped variable with the same name as an outer scoped variable, the value of the local scoped variable takes priority. As an example:

// global scope
var foo = 'bar';

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

console.log(foo); // 'bar'
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

let orconst` 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;
}
Enter fullscreen mode Exit fullscreen mode

A variable defined with let, const, and var, on the other hand, can cast a shadow on a global-scoped let, const, and var variable. As an example:

// global scope
const foo = 'bar';

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

console.log(foo); // 'bar'
Enter fullscreen mode Exit fullscreen mode

Conclusion

Variable shadowing is prone to causing confusion and unforeseen consequences, as well as undesirable faults in your code. Consider the following example, in which the variable "name" is shadowed:

let name = 'John';

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

console.log(name); // 'John'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Variable shadowing is prone to causing confusion and unforeseen consequences, as well as undesirable faults in your code. Consider the following example, in which the variable "name" is shadowed:

  • 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.

Discussion (4)

Collapse
afsana1313 profile image
Afsana

Is this a typo?

Collapse
0xf10yd profile image
Muhammad Ahmad Author

What exactly?

Collapse
afsana1313 profile image
Afsana

I had uploaded an image/screenshot. But I didn't notice it wasn't uploaded. My mistake, sorry for that.

This is the exerpt I got from your article "Variable Shadowing in JavaScript". So, I wanted to ask if "2. 2." is a typo.

Variable Type and Scope Determine If the Value of Shadowed 2. 2. Variable Is Overwritten;
Local Scope Variables Take Precedence;

Thread Thread
0xf10yd profile image
Muhammad Ahmad Author

Oh, yes, it's a typo, I've just noticed that one and made an edit, thanks for lettine know:)