Best Practices in Declaring Variables in JavaScript

6

In the MDN variables section we have the following statement:

  

For that reason, it is recommended to always declare variables at the   top of their scope (the top of global code and the top of function   code) so it's clear which variables are function scoped (local) and   which are resolved on the scope chain.

This excerpt says that it is always recommended to declare variables at the top of their scope because JavaScript has the variable hoisting behavior.

What are the implications of not following this recommendation? In particular, I find a code much easier to read when the declaration of your variable is performed at the time exactly prior to its first use, compared to declaring all variables at the start of a function's execution, for example.

Exemplifying:

// Declarando variável no topo de seu escopo
function(){
    var a = 0, b = 0;
    //some code
    a = 5;
    //some code
    b = 10;
}
//Declarando variável próximo de sua utilização
function(){
    //some code
    var a = 0;
    a = 5;
    //some code
    var b = 0;
    b = 10;
}
    
asked by anonymous 13.12.2016 / 20:26

3 answers

7

If you are aware of how hoisting works , the biggest implications are for other people who might mess around in your code.

I believe this recommendation is mainly based on the consequences of variables captured by closures. For example:

var fns = [];

for(var i=0; i<5; i++) {
  // cria closures
  fns.push(function() {
    console.log('closure sobre i com valor ', i)
  });

}

for(var j=0; j<fns.length; j++) {
  fns[j]();
}

The above code will always log 5 as the value of i in the console, because there is only one i variable, and 5 is its value after the loop. This confuses a lot of people, there are numerous questions on the subject here on the site. Part of this confusion is because people expect the variable declared at the for cipher initializer to have scope restricted to the for block, when in fact it is in the outer scope.

Compare to ES-2015, using let to declare variables with block scope:

var fns = [];

for(let i=0; i<5; i++) {
    // cria closures
    fns.push(function() {
        console.log('closure sobre i com valor ', i)
    });

}

for(var j=0; j<fns.length; j++) {
    fns[j]();
}

JSFiddle

In this version, each call logs a different value, which was the value of i at the time of creation of each closure. This indicates that with let at each iteration of for there was a variable i distinct (versus a single i with var ). So it has the behavior expected by those who are accustomed to block scope.

In fact, in ES-2015 it is recommended to place declarations with let always just before use, precisely because it does not create the same confusion as var .

    
13.12.2016 / 21:07
6

hoisting problems

In fact you run the risk of hoisting if you do not organize the code well . If the pain function so complicated then maybe the problem is another. Note that organizing the right code is no problem at all. It's just the risk of falling into a problem situation unintentionally. If you have any tool that helps you analyze code, you can prevent it from accidentally flipping.

I will not go into detail because the link above already explains better what the hoisting effect is, but in short, the variable, superficially speaking, is created at the beginning of execution of the function, then declare it there is to make sure that the code expresses the reality of what will be executed. If you declare elsewhere, it may not be what you expect. Problem example:

var x = 0;
function hoisting() {
    console.log(x);
    var x = 1;
    console.log(x);
}
function nonHoisting() {
    var x = 1;
    console.log(x);
}
function nonHoisting2() {
    console.log(x);
}
hoisting();
nonHoisting();
nonHoisting2();

Global Scope

I do not recommend using global scope, but if I do, I think I'd better declare everything first. The mess is already made, so you better organize it the best you can, it's very easy to make a slip in that case. You have placed a function that uses this variable before it has been declared and may already have surprises.

let

This only occurs with var . With let this risk does not run. This is why I find it more interesting whenever possible (if you are going to use a new version of JS or if it is going to be a translator, it was created to solve a problem that the language had.)

So you can do what really is most recommended in most cases, which is to declare the variable close to where it will be used. With var , or be very careful or can have hoisting .

function teste(){
    //some code
    let a = 0;
    a = 5;
    //some code
    let b = 0;
    b = 10;
  console.log(a + b);
}
teste();
    
13.12.2016 / 21:04
4

What are the implications of not following this recommendation? : This has to do with clarity and code organization.

Nowadays there is let and const to declare variables, and they give error if the variable is declared twice within the same scope or block.

With var of these scenarios are possible:

var a = 10;
// outras linhas de código aqui...
// e depois, misturado algures dentro da mesma função:
var a = function(){}

That is, we can in the same scope declare a twice without being warned that the first a was overwritten!

If we put all the variable declarations at the beginning of the scope, it would be easier to detect this:

var a = 10;
var a = function(){} // <--- oops, "a" já foi declarado
// outras linhas de código aqui...

With let or const the above code would give error:

  

Uncaught SyntaxError: Identifier 'a' has already been declared

The other reason is hoisting . That is, in practice the variables are declared at the top of the scope. Even though we have var a = b; in the middle of the code the JavaScript interpreter will always read this way:

var a; // declare and remain undefined // the rest of code a = b; // the value is assigned only when execution arrives at that line of code.

example:

console.log(a); // undefined
try {
    console.log(b); // vai dar erro
} catch (e) {
    console.log(e); // erro: ReferenceError: b is not defined
}
var a = 10;
console.log(a); // 10

Now to avoid confusion and for the code to reflect what happens inside the interpreter, you are advised to declare the variables at the top of the scope / block.

    
16.12.2016 / 08:52