Front End

18 out, 2016

Entendendo Hoisting e escopo, o que mudou no ES2015 – Parte 02

Publicidade

Comecei recentemente uma série para entendermos hoisting e escopo no ES2015. Se você não viu, clique aqui. Na primeira parte, falamos sobre escopo, e a ideia hoje é falar sobre hoisting. Vamos lá?

O que é Hoisting?

Hoisting: Levante (algo) por meio de cordas e polias. Sinônimos: levantar, levantar-se, elevador, transportar-se, erguer-se, subir, suba, guincho para cima, puxar para cima, elevar.

Em tempo de execução, todas as variáveis e declarações de funções são movidas para o início de cada função (seu próprio escopo), independentemente de onde eles são declarados  –  isso é conhecido como elevação (hoisting). Dito isso, é uma boa prática declarar todas as variáveis juntas na primeira linha, a fim de evitar falsas expectativas em relação a uma variável que foi declarada depois, mas que acabou recebendo um valor antes.

No entanto, somente a declaração será hoisted (elevada). Se a variável é também inicializada, o valor no topo do escopo será inicialmente configurado para undefined.

JavaScript Hoisting

Vamos vê-lo em ação:

(function() {
  var x = 'Vamos ver e entender ';
  ...
  // linhas de código
  ...
  var y = 'como nosso código ';
  ...
  // Mais algumas linhas de código
  ...
  var z = 'é interpretado e executado';
  ...
  // Centenas de linhas de código
  ...

Que é interpretado e executado:

(function() {
  var x, y, z; // variáveis declaradas

  x = 'Vamos ver e entender '; // inicializado
  ...
  // linhas de código
  ...
  y = 'como nosso código '; // inicializado
  ...
  // Mais algumas linhas de código
  ...
  z = 'é interpretado e executado'; // inicializado

E por que eu devo me importar?

var text = "Javascript Roots !!!";
(function () {
    console.log("The value of variable: " + text);
})();

O código acima está todo ok até o momento. Vamos fazer algumas modificações.

var text = "Javascript Roots !!!";
(function () {
    console.log("The value of variable: " + text);
    var text = "I love Javascript !!!"
    console.log("The new value of varible: " + text);
})();

O primeiro console vai te retornar The value of variable: undefined e o segundo, The new value of varible: I love Javascript!!! Para muitos, o valor undefined é bem estranho, principalmente para desenvolvedores C (e sua família), além dos novos com JavaScript. Mas vamos entender o porquê disso:

var text = “Javascript Roots !!!”;
(function () {
 var text; // declaração
 console.log(“The value of variable: “ + text);
 text = “I love Javascript !!!” // inicializado
 console.log(“The new value of varible: “ + text);
})();

Eu sei que é bem estranho, mas por padrão devemos adotar que toda declaração de variáveis fiquem no início do seu código, evitando bugs.

Function Hoisting

As funções também são elevadas (hoisted):

minhaFuncaoHoisting(); // Outputs: “Sim!”

function minhaFuncaoHoisting() {
  console.log(“Sim!”);
}

O interpretador de JavaScript permite que você use uma função antes de ser declarado no código-fonte.

No entanto, a função de elevação ocorre apenas para funções declaradas, e não para funções anônimas.

fnHoisting();
// Sim esta função é hoisting =D

fnNotHoisting();
// TypeError: fnNotHoisting is not a function

function fnHoisting() {
  console.log(“Sim esta função é elevada =D”);
}

var fnNotHoisting = function() {
  console.log(“Esta função não é elevada =/”);
}

No exemplo de código acima, vemos a interação de dois tipos diferentes de hoisting. Nossa variável fnNotHoisting tem a sua declaração hoisting, mas não o seu valor, que é undefined. Funções anônimas não são hoisting.

E o que mudou no ES2015?

Bem, com o ES2015, uma das boas mudanças foi a chegada de variáveis com let e escopo. Essa novidade permite declarar variáveis limitadas no âmbito de um bloco. É o oposto do var, por declarar e sobrescrever variáveis no âmbito global de sua função envolvente.

O let vai fazer hoisting da variável no topo do bloco, ou seja, caso seja declarado novamente no mesmo contexto, a segunda declaração retornará um TypeError. E caso a variável definida com let seja referenciada antes da sua declaração, o erro será de referência.

Vamos ver um exemplo com let:

function helpMe() {
    var help = 'Me ajude, localização -> ';

    if(help) {
        var location = '-23.5193987,-46.1895406';
    }
    var phrase = help + location;
    console.log(phrase); //Me ajude, localizaçã -> -23.5193987,-46.1895406
}
helpMe();

function imHere() {
    let im = 'Eu estou aqui ->'

    if (im) {
        let here = '-23.5193987,-46.1895406';
    }
    let phrase = im + here; 
    console.log(phrase) //ReferenceError: here is no defined

No código acima, temos as funções helpMe() e imHere(). Na função helpMe, a variável location pode ser acessada fora do bloco if. Por isso, quando chamamos a variável phrase, é retornado o resultado:

Me ajude, localização -> -23.5193987,-46.1895406

Diferentemente, na função imHere, a variável here nos retorna:

ReferenceError: here is not defined.

Isso acontece porque ela está definida dentro do bloco if, podendo ser acessada somente nesse bloco.

Diferença entre var e let

Com var, é possível redeclarar a mesma variável, mas o mesmo não acontece com let. Vamos ver um exemplo:

var x = 'foo1';
let y = 'bar1';

var x = 'foo2';
let y = 'bar2'; // SyntaxError: Identifier 'y' has already been declared
console.log(x);
console.log(y)

Com var, podemos ler a variável mesmo que o código ainda não tenho sido inicializado. Com var, isso vai retornar undefined; com let, vai te retornar um erro. Veja a seguir:

console.log(x);
var x = 'foo';

console.log(y); // ReferenceError: y is not defined
let y = 'foo';

Outro algo novo que surgiu é a Const. Em ES6, uma const representa uma referência constante para um valor. Em outras palavras, o valor não está congelado, apenas a atribuição dele.

{
    const ARR = [01, 02];
    ARR.push(10);
    console.log(ARR); // [01,02,10]
    ARR = 11; // TypeError
    ARR[0] = 03; // valor não é imutável
    console.log(ARR); // [03,02,10]
}

Algumas coisas para lembrar:

  • let e const são escopadas aos fechamentos de bloco mais próximos;
  • Quando usar const, use CAPITAL_CASING;
  • hoisting” de let e const varia da forma tradicional de “hoisting” de variáveis e funções. Ambos estão “hoistados”, mas não podem ser acessados antes das suas declarações.
  • const deve ser definida na sua declaração.

Ficou alguma dúvida ou tem algum comentário? Aproveite os campos abaixo. Até a próxima!

***

Artigo publicado originalmente em http://www.concretesolutions.com.br/2016/09/13/hoisting-e-escopo-es2015-2/.