Javascript - Um pouco sobre váriáveis

Publicado em: 01/06/2020

Existem diversas maneiras de declarar variáveis no Javascript e a especificação ECMAScript 6 adicionou mais algumas bastante interessantes que hoje se tornaram padrão no desenvolvimento de aplicações modernas com Javascript.

Var

Podemos usar a palavra reservada var para declarar uma variável que contém diversos tipos diferentes:

var usuario = 'Flynn';
var lifepoints = 99;
var itens = ['Poção de Mana', 'Espada de Ferro', 'Escudo'];
var inventario = {
    maos: 'Espada de Ferro',
    pernas: 'Botas de ferro +5'
    corpo: 'Armadura de cobre +20'
}

var chefeFoiDerrotado = false;

O uso desse tipo de variável, porém, vem com alguns efeitos colaterais referente ao escopo da variável, que nesta possui um escopo imediatamente relativo a função que a cerca, mas não de bloco, e por isso, deve ser evitado quando possível para minimizar bugs. Veja abaixo um exemplo:

function novaAventura() {
  // variáveis que podem sem acessadas em todo o escopo da
  // função.
  var nome = "gughog";
  var classe = "Dark Mage";

  console.log('Nome: ', nome); // gughog
  console.log('Classe: ', classe); // Dark Mage

  {
    var segredo = "Não sabe usar magias elétricas";
    console.log('Alguém próximo ouviu:' , segredo); // Não sabe usar magias elétricas
  }

  console.log('Algué distante ouviu: ', segredo); // Não sabe usar magias elétricas
}

novaAventura();

No exemplo acima, "alguém próximo" refere-se a um escopo, e "alguém distante" se refere a outro. Utilizando a palavra reservada var em ambos os escopos será possível saber o valor da variável segredo pois o var é acessível em todos os escopos, não ultrapassando apenas o escopo de uma função. Veja abaixo:

function bolaDeFogo () {
  var dano = 50
  var multiplicador = 5.5 // suponhamos que esse valor é estático

  console.log('Dano de fogo: ', dano * multiplicador)
}

function bafoGelado () {
  var dano = 40
  // e se além de re declarar "multiplicador", apenas peguemos
  // "emprestado" da função anterior ?
  console.log('Dano de gelo: ', dano * multiplicador)
}

bolaDeFogo() // 275
bafoGelado() // ReferenceError: multiplicador is not defined

Ué, mas porque isso aconteceu ? Bem, o escopo de var é limitado pelo escopo de uma função que a contém, isso significa que se tentarmos acessar outras variáveis que estão dentro de funções teremos um belo de um erro de referência. Dentro de uma função é diferente: qualquer var declarada dentro dela é acessível por qualquer escopo.

O ECMAScript 6 nos trouxe duas novas formas de declarar variáveis que mudaram bastante a dinâmica de como variáveis funcionam no Javascript.

Let

Utilizando-se do let, temos variáveis que podem ser reatribuidas (isto é, podemos mudar seus valores sobreescrevendo-as), porém dentro de uma série de restrições, de modo a evitar efeitos colaterais e problemas no desenvolvimento. Um deles é a limitação de escopo por bloco. Vamos retornar ao nosso exemplo anterior, porém refatorando tudo de modo a usar apenas o let:

function novaAventura() {
  // variáveis que podem sem acessadas em todo o escopo da
  // função.
  let nome = "gughog";
  let classe = "Dark Mage";

  console.log('Nome: ', nome); // gughog
  console.log('Classe: ', classe); // Dark Mage

  {
    let segredo = "Não sabe usar magias elétricas";
    console.log('Alguém próximo ouviu:' , segredo); // Não sabe usar magias elétricas
  }

  console.log('Algué distante ouviu: ', segredo); // ReferenceError: segredo is not defined
}

novaAventura();

Ué, mas tava funcionando na minha máquina!

O que aconteceu aqui é que a variável segredo que definimos dentro das chaves pertente ao escopo das chaves. Ou seja, existe agora uma limitação de escopo por bloco, isso quer dizer que tudo declarado dentro de chaves, ou de um bloco condicional, ou de qualquer outro bloco é acessível apenas neste bloco.

Nesse exemplo, usando a mesma analogia do exemplo, "alguém próximo" consegue saber o segredo, lá que ele foi "dito" no escopo dele, mas "alguém distante" não tem acesso ao segredo mais, devido ao let limitar o escopo ao bloco, deixando "alguém distante" na curiosidade.

Outra coisa peculiar do let é que nós não podemos declarar duas variáveis com o mesmo nome e esperar que a mais nova sobreescreva a anterior, como acontece com var, veja:

  // usando "var"
  var dano = 50
  var dano = 0

  console.log(dano) // 0
  
  // usando "var"
  let dano = 50
  let dano = 0

  console.log(dano) // SyntaxError: Identifier 'dano' has already been declared

Nesse caso o que podemos fazer é reatribuir, mas não declarar duas com o mesmo nome:

  let dano = 50
  dano = 0

  console.log(dano) // 0

Const

Chegamos na última forma, e uma das mais interessantes: a const (de constante) é um tipo de variável do Javascript cujo qual depois de declarada não pode mais ser reatribuida ou modificada, ou seja, como seu nome já diz, ela é uma constante. Veja:

  // tentando redeclarar:
  const PI = 3.14
  const PI = 8000

  console.log(PI) // SyntaxError: Identifier 'PI' has already been declared
  
  // então será que reatribuindo vai ?
  const PI = 3.14
  PI = 8000

  console.log(PI) // TypeError: Assignment to constant variable.

No exemplo acima, o valor de PI ficou protegido, ao invés de permitir a sobreescrição ou a reatribuição, ao perceber que estamos tentando reatribuir um valor constante o console levantou um erro, para que estejamos ciente que a operação não é válida.

Uma dica ao declarar constantes é que, como sugerem alguns guias de estilo, que elas sejam escritas em CAIXAALTA. Não é obrigatório, porém recomendado, para uma maior organização visual do seu código.

Antes que você pense que a const é a bala de prata para criar coisas imutáveis, sinto dizer que infelizmente não. De fato, o valor guardado em uma const não é alterado, porém, se temos por exemplo um objeto guardado numa const, conseguimos alterar livremente suas propriedades:

  const ficha = {
    name: 'gughog',
    classe: 'Dark Mage',
    level: 2
  }

  ficha.level = 99

  console.log(ficha) // 99

No exemplo acima, conseguimos mudar o level do nosso personagem para 99, apenas acessando a propriedade "level", usando a notação de ponto (do inglês dot notation), e sobreescrevemos o seu valor. Perceba que isso só foi possivel pois mudamos uma propriedade do valor, e não ele completamente. Para provar, tentamos então mudar o valor de ficha:

  const ficha = {
    name: 'gughog',
    classe: 'Dark Mage',
    level: 2
  }

  ficha = {}

  console.log(ficha) // TypeError: Assignment to constant variable.

Mas e como posso deixar até mesmo objetos imutáveis ?

Existe um método de deixar objetos e suas propriedades totalmente imutáveis, para isto, leia o nosso post Como tornar objetos e propriedades imutáveis no Javascript.

Considerações finais

Claramente o assunto sobre declarações de variáveis no Javscript é muito extenso, com muitas peculiaridades e controvérsias, mas acredito que este post possa ter te ajudado a ter uma noção de algumas nuances que cercam esse tema. Até o próximo post!