loading...

JavaScript Scopes

kayis profile image K ・2 min read

If you started working with JavaScript before the release of ES2015, you are probably used to the fact, that JavaScript variables are function-scoped and hoisted etc.. With ES2015, you get a new way to define variables that are block-scoped.

Before ES2015

Back in the days var was the keyword of choice to define a local variable, if you didn't use var and used a new indentifier for assignment, you could create a global variable, sometimes even by accident, if you didn't use the "use strict"; statement.

function () {
    // Global variable
    newIdentifier = 5;

    // Local variable
    var variable = 6;

    // Assignment to the defined variable
    variable = 8;

    // Another global variable (by accident)
    varialbe = 8; 
}
Enter fullscreen mode Exit fullscreen mode

Your local variables were scoped by the function in which they were defined.

// You could write something like this:
function (x) {
    if(x > 2) {
    var y = 10 * x
    }

    return y
}

// But behind the scenes it would be treated as this:
function (x) {
    var y
    if(x > 2) {
    y = 10 * x
    }

    return y
}
Enter fullscreen mode Exit fullscreen mode

This lead many developers to define all the local variables at the top of the function, because they would end up there anyway.

Since ES2015

With the release of ES2015 JavaScript got many new features, one of them block scoped variables. There are two kind of them, let and const variables.

// So this wouldn't work:
function (x) {
    if(x > 2) {
    let y = 10 * x
    }

    return y
}
Enter fullscreen mode Exit fullscreen mode

In the example, y is only accessible inside the if-block, which is the default behavior of many other languages.

What this allows you to do is define variables where they are needed and scope them with code blocks.

// Every time you see a { and } you're creating a new block
// and in there you can create a new variable scoped to that block
while(...) {
    let x = 10;
}

for(...) {
    let x = 12;
}

if (...) {
    let x = 9;
} else {
    let x = 8;
}

try {
    let x = 1;
} catch (e) {
    let x = 99;
}
Enter fullscreen mode Exit fullscreen mode

You can even use {} on their own for scoping, to keep the vars as local as possible.

function () {
    let varForTheWholeFunction = 10;

    {
    let scopedVar = getData();
    varForTheWholeFunction = parse(scopedVar);
    }
    // scopedVar isn't accessible here
    ...
    {
    let scopedVar = filterData(varForTheWholeFunction);
    varForTheWholeFunction = sortData(scopedVar);
    }

    return varForTheWholeFunction;
}
Enter fullscreen mode Exit fullscreen mode

This can be used for switch statements too.

function () {
    let a;

    switch(x) {
    case "getData": {
        let y = getData();
        a = parse(y);
    } break;
    ...
    default: {
        let y = "defaultData";
        a = parse(y);
    }
    }

    return a;
}
Enter fullscreen mode Exit fullscreen mode

So what about const? Well, their scoping works like with let, but a value has to be assigned at definition time and it can't change later, only if the function, in which this local variable is declared, is called again.

function (x) {
    if (x > 2) {
    // This will fail:
    const a;

    // This will also fail:
    const a = 1;
    a = 2;
    }
}
Enter fullscreen mode Exit fullscreen mode

So it's a variable that can only be set once, but be careful:

function () {
    // This would work:
    const x = {y: 1};
    x.y = 2;
}
Enter fullscreen mode Exit fullscreen mode

Indirections aren't save from mutation.

Conclusion

With let and const JavaScript got a way to define scopes more fine granular, which enables developers to limit code dependencies even more.

The cost of this is added complexity to the language, but on the other hand, you don't have to use all of the features that exist :)

Discussion

pic
Editor guide
Collapse
jmourtada profile image
Jonathan Mourtada

The cost of this is added complexity to the language

One can argue about this :) I would say that let and const makes Javascript simpler and that var adds complexity to the language. Most developers expects variables to be blocked scoped. But then again you should really learn the language first and then the concept function scope shouldn't be a problem.

I'm using a linting rule to disallow the use of var in my code base because i see no advantage of using it.

On more case that is tricky with var is when deferring execution.

var functions = [];
for (var i=0;i<3;i++) {
    functions[i] = function() {
        console.log(i);
    }
}

functions[0](); // 3
functions[1](); // 3
functions[2](); // 3
Collapse
xiaohuoni profile image
xiaohuoni

var functions = [];
for (var i=0;i<3;i++) {
functions[i] = function(i) {
console.log(i);
}(i)
}

functions0; // 0
functions1; // 1
functions2; // 2

Collapse
amanb014 profile image
Aman Bhimani

Nice post! If anyone needs any more clarifications on scopes, I really recommend reading this book here (free!) hosted on GitHub. It's a really nice read, a bit dense (lots of information, so read carefully!
github.com/getify/You-Dont-Know-JS...

Collapse
damcosset profile image
Damien Cosset

Did you find any use cases where var is actually a good idea? Or is this keyword totally useless now that let and const have been added?

Collapse
kayis profile image
K Author

just compat reasons. If you want make something that runs in all browsers without compilation, you would go for var.

But I guess this is just a thing for tiny libs.

Collapse
andrewdtanner profile image
Andrew Tanner 🇪🇺

Good, simple explanation - thanks!