Durante o desenvolvimento de uma aplicação com JavaScript, utilizamos variáveis para armazenas os dados.
Muitas vezes, criamos as variáveis de forma automática de acordo com a experiencia que tivemos em outros projetos. Sabemos que em um determinado contexto, devemos usar uma const
e em outros devemos usar let
.
É importante conhecer cada variável e suas diferenças, para que possamos utilizar aquela que faça mais sentido em cada parte do nosso código.
Atualmente, o JavaScript possui 4 tipos de variáveis.
const
let
var
não declarada
Neste artigo, vamos passar por cada uma delas, com exemplos e explicações. Vamos entender como elas são diferentes uma das outras e quando devemos usar cada uma!
ESCOPOS
Cada variável pode ter um tipo de escopo, dependendo de onde ela é declarada.
Atualmente, o JavaScript possui 3 tipos de escopo:
- Escopo Global
- Escopo de Função
- Escopo de Bloco (introduzido na versão ES6 – 2015)
Fazendo um breve resumo dos tipos de escopo que cada variável pode ter:
var
: Escopo Global – Escopo de Função;let
: Escopo Global – Escopo de Função – Escopo de Bloco;const
: Escopo Global – Escopo de Função – Escopo de Bloco;não declarada
: Escopo Global;
HOISTING (elevação)
O hoisting pode ser um pouco confuso de entender, mas em termos teóricos, é como se, ao declarar uma variável, essa declaração fosse movida para o inicio do escopo em que foi declarada.
Por exemplo, a declaração de uma variável no escopo global, fará com ela que ela seja movida para o inicio do código enquanto a declaração de uma variável dentro de uma função, fará com que ela seja movida para o inicio da função, pois ela pertence ao escopo da função.
Um ponto importante sobre o hoisting, é que ele só move as declarações. As inicializações não são movidas. Mas não se preocupe, vamos entender melhor como isso afeta cada declaração.
É comum dizer que a variável está na Temporal Dead Zone (TDZ) desde o inicio do código até que sua declaração seja processada.
Por último, o que acontece na prática, é que as declarações são guardadas em memória durante a fase de compilação e permanecem no mesmo lugar que foram colocadas no código.
VAR
Variáveis declaradas com var
, podem ter Escopo Global ou Escopo de Função.
Escopo Global
Sempre que uma var
for declarada do lado de fora de uma function
, ela pertence ao Escopo Global e pode ser acessada em qualquer parte do código.
var fruta = "Abacaxi"; // Escopo Global
function x() {
console.log(fruta); // Abacaxi
}
x();
console.log(fruta); // Abacaxi
No contexto de um navegador, ao pertencer ao Escopo Global, uma var
se torna propriedade do Objeto Global window.
var nome = "João"; // Escopo Global
function x() {
var fruta = "Maça"; // Escopo de Função
}
x();
console.log(window.nome); // João
console.log(window.fruta); // undefined
Escopo de Função
Sempre que uma var
for declarada dentro de uma function
, ela pertence ao Escopo de Função e não pode ser acessada do lado de fora.
function x() {
var fruta = "Abacaxi"; // Escopo de Função
console.log(fruta); // Abacaxi
}
x();
console.log(fruta); // ReferenceError: fruta is not defined
Escopo de Bloco
Uma variável declarada com var
NÃO pode ter Escopo de Bloco. Quando ela é encontrada dentro de um bloco, sempre terá o Escopo Global ou Escopo de Função, dependendo de onde foi declarada.
var cor = "Verde"; // Escopo Global
if (true) {
var fruta = "Pêssego"; // Escopo Global
}
function x() {
var animal = "Gato"; // Escopo de Função
if (true) {
var fruta = "Pêssego"; // Escopo de Função
}
}
x();
Hoisting
As declarações de var
sempre serão processadas antes da execução de qualquer código.
Ou seja, ao declarar uma var
, o hoisting eleva sua declaração para o inicio de seu escopo e a inicializa com o valor de undefined
.
Este código:
var cor = "Amarela";
console.log(cor); // Amarela
Corresponde indiretamente a:
var cor;
cor = "Amarela";
console.log(cor); // Amarela
Da mesma forma que este código:
function x() {
var cor = "Amarela";
console.log(cor); // Amarela
}
x();
Corresponde indiretamente a:
function x() {
var cor;
cor = "Amarela";
console.log(cor); // Amarela
}
x();
A inicialização da variável não é elevada. Somente a declaração.
Devido a esse comportamento, o valor undefined
é atribuído à variável.
Este código:
console.log(cor); // undefined
var cor = "Amarela";
console.log(cor); // Amarela
Corresponde indiretamente a:
var cor = undefined;
console.log(cor); // undefined
cor = "Amarela";
console.log(cor); // Amarela
Da mesma forma que esse código:
cor = "Azul";
console.log(cor); // Azul
var cor = "Amarela";
console.log(cor); // Amarela
Corresponde indiretamente a:
var cor = undefined;
cor = "Azul";
console.log(cor); // Azul
var cor = "Amarela";
console.log(cor); // Amarela
Redeclaração
Uma variável var
pode ser redeclarada.
var animal = "Gato";
console.log(animal); // Gato
var animal = "Cachorro";
console.log(animal); // Cachorro
Atribuição Opcional
Podemos declarar uma var
sem atribuir um valor inicial.
var fruta;
fruta = "Abacaxi";
Reatribuição
Utilizar var
nos permite reatribuir um valor sempre que necessário.
Por exemplo:
var contador = 100;
console.log(contador); // 100
if (true) {
contador = 0;
}
console.log(contador); // 0
LET
Variáveis declaradas com let
, podem ter Escopo Global, Escopo de Função ou Escopo de Bloco.
Escopo Global
Sempre que uma let
for declarada do lado de fora de uma function
ou bloco ({ }
), ela pertencerá ao Escopo Global e pode ser acessada em qualquer parte do código.
let nome = "Pedro";
function x() {
console.log(nome); // Pedro
}
x();
console.log(nome); // Pedro
Uma let
NÃO pertence ao objeto global.
Escopo de Função
Sempre que uma let
for declarada dentro de uma function
, ela pertence ao Escopo de Função e não pode ser acessada do lado de fora.
function x() {
let fruta = "Abacaxi"; // Escopo de Função
console.log(fruta); // Abacaxi
}
x();
console.log(fruta); // ReferenceError: fruta is not defined
Escopo de Bloco
Sempre que uma let
for declarada dentro de um bloco { }
, ela pertence ao Escopo de Bloco e não pode ser acessada do lado de fora.
function x() {
let animal = "Gato"; // Escopo de Função
if (true) {
let fruta = "Pêssego"; // Escopo de Bloco
console.log(fruta); // Pêssego
}
console.log(animal); // Gato
console.log(fruta); // ReferenceError: fruta is not defined
}
x();
Observe outros exemplos:
for (let index = 1; index <= 10; index++) {
console.log(index); // de 1 até 10
}
console.log(index); // ReferenceError: index is not defined
if (true) {
let cor = "Verde";
console.log(cor); // Verde
} else {
let cor = "Laranja";
console.log(cor); // Laranja
}
console.log(cor); // ReferenceError: cor is not defined
Hoisting
Ao declarar uma let
, o hoisting eleva sua declaração e NÃO a inicializa até que ela sai Temporal Dead Zone. Por conta disso, não é possível acessar uma let
antes de sua inicialização.
console.log(nome);
let nome = "João"; // ReferenceError: Cannot access 'nome' before initialization
No caso da declaração SEM inicialização, o valor undefined
é atribuído.
let nome;
console.log(nome); // undefined
Redeclaração
Uma variável let
não pode ser redeclarada.
let cor = "Azul";
let cor = "Verde"; // SyntaxError: Identifier 'cor' has already been declared
Atribuição Opcional
Podemos declarar uma let
sem atribuir um valor inicial.
let fruta;
fruta = "Abacaxi";
Reatribuição
Utilizar let
nos permite reatribuir um valor sempre que necessário.
Por exemplo:
let contador = 100;
console.log(contador); // 100
if (true) {
contador = 0;
}
console.log(contador); // 0
CONST
Variáveis declaradas com const
, podem ter Escopo Global, Escopo de Função ou Escopo de Bloco.
Escopo Global
Sempre que uma const
for declarada do lado de fora de uma function
ou bloco ({ }
), ela pertencerá ao Escopo Global e pode ser acessada em qualquer parte do código.
const nome = "Pedro";
function x() {
console.log(nome); // Pedro
}
x();
console.log(nome); // Pedro
Uma const
NÃO pertence ao objeto global.
Escopo de Função
Sempre que uma const
for declarada dentro de uma function
, ela pertence ao Escopo de Função e não pode ser acessada do lado de fora.
function x() {
const fruta = "Abacaxi"; // Escopo de Função
console.log(fruta); // Abacaxi
}
x();
console.log(fruta); // ReferenceError: fruta is not defined
Escopo de Bloco
Sempre que uma let
for declarada dentro de um bloco { }
, ela pertence ao Escopo de Bloco e não pode ser acessada do lado de fora.
function x() {
const animal = "Gato"; // Escopo de Função
if (true) {
const fruta = "Pêssego"; // Escopo de Bloco
console.log(fruta); // Pêssego
}
console.log(animal); // Gato
console.log(fruta); // ReferenceError: fruta is not defined
}
x();
Observe outros exemplos:
Tanto o if
como o else
são considerados blocos diferentes:
if (true) {
const cor = "Verde";
console.log(cor); // Verde
} else {
const cor = "Laranja";
console.log(cor); // Laranja
}
console.log(cor); // ReferenceError: cor is not defined
Neste caso, as declarações feitas estão dentro do escopo de bloco do switch:
const cor = "Rosa";
switch (cor) {
case "Azul":
const cor = "Azul";
console.log(cor);
break;
case "Verde":
const cor = "Verde"; // SyntaxError: Identifier 'cor' has already been declared
console.log(cor);
break;
default:
console.log("Cor " + cor + " não encontrada.");
}
Uma maneira de contornar esta situação, é adicionando o escopo de bloco { }
para cada case
:
const cor = "Rosa";
switch (cor) {
case "Azul": {
const cor = "Azul";
console.log(cor); // Azul
break;
}
case "Verde": {
const cor = "Verde";
console.log(cor); // Verde
break;
}
default: {
console.log("Cor " + cor + " não encontrada."); // Cor Rosa não encontrada.
}
}
Hoisting
Ao declarar uma const
, o hoisting eleva sua declaração e NÃO a inicializa até que ela sai Temporal Dead Zone. Por conta disso, não é possível acessar uma const
antes de sua inicialização.
console.log(nome); // ReferenceError: Cannot access 'nome' before initialization
const nome = "João";
Redeclaração
Uma const
não pode ser redeclarada.
const nome = "João";
const nome = "Jean"; // SyntaxError: Identifier 'nome' has already been declared
Atribuição Obrigatória
Ao declarar uma const
, é obrigatório a atribuição de um valor.
const nome; // SyntaxError: Missing initializer in const declaration
Reatribuição
Uma const
não pode ser reatribuída.
Isso ocorre porque é criada uma referencia imutável ao valor. Ou seja, você não pode alterar o valor que essa variável referencia.
const pi = 3.14159265358979323846;
pi = 3.14; // TypeError: Assignment to constant variable
Neste exemplo, a declaração da const
cria uma referencia de pi para o valor 3.14159265358979323846.
Quando tentamos reatribuir o valor de pi para 3.14, estamos tentando alterar a referencia de pi.