Este artigo é para entender scripts antigos
As informações neste artigo são úteis para compreender scripts antigos.
Não é assim que escrevemos novo código.
No primeiro capítulo sobre variáveis, mencionamos três formas de declaração de variáveis:
let
const
var
A declaração var
é semelhante a let
. Na maioria das vezes podemos substituir let
por var
ou vice-versa e esperar que tudo funcione:
var mensagem = "Olá"; alerta(mensagem); // Oi
Mas internamente var
é uma fera muito diferente, que vem de tempos muito antigos. Geralmente não é usado em scripts modernos, mas ainda se esconde nos antigos.
Se você não planeja cumprir tais roteiros você pode até pular este capítulo ou adiá-lo.
Por outro lado, é importante entender as diferenças ao migrar scripts antigos de var
para let
, para evitar erros estranhos.
Variáveis, declaradas com var
, têm escopo de função ou escopo global. Eles são visíveis através de blocos.
Por exemplo:
se (verdadeiro) { var teste = verdadeiro; // use "var" em vez de "let" } alerta(teste); // verdadeiro, a variável permanece após if
Como var
ignora blocos de código, temos uma variável global test
.
Se usássemos let test
em vez de var test
, a variável só seria visível dentro if
:
se (verdadeiro) { deixe testar = verdadeiro; //use "deixe" } alerta(teste); // ReferenceError: teste não está definido
A mesma coisa para loops: var
não pode ser local de bloco ou loop:
for (var i = 0; i < 10; i++) { var um = 1; // ... } alerta(eu); // 10, "i" fica visível após o loop, é uma variável global alerta(um); // 1, "one" fica visível após o loop, é uma variável global
Se um bloco de código estiver dentro de uma função, var
se tornará uma variável em nível de função:
function digaOi() { se (verdadeiro) { var frase = "Olá"; } alerta(frase); // funciona } digaOi(); alerta(frase); // ReferenceError: frase não está definida
Como podemos ver, var
atravessa if
, for
ou outros blocos de código. Isso porque há muito tempo, em JavaScript, os blocos não tinham ambientes lexicais, e var
é um resquício disso.
Se declararmos a mesma variável com let
duas vezes no mesmo escopo, isso será um erro:
deixe o usuário; deixe o usuário; // SyntaxError: 'user' já foi declarado
Com var
, podemos redeclarar uma variável quantas vezes quiser. Se usarmos var
com uma variável já declarada, ela será simplesmente ignorada:
var usuário = "Pete"; var usuário = "João"; // este "var" não faz nada (já declarado) // ...não aciona um erro alerta(usuário); // John
As declarações var
são processadas quando a função é iniciada (ou o script é iniciado para globais).
Em outras palavras, as variáveis var
são definidas desde o início da função, não importa onde esteja a definição (assumindo que a definição não esteja na função aninhada).
Então este código:
function digaOi() { frase = "Olá"; alerta(frase); frase var; } digaOi();
…É tecnicamente igual a isto ( var phrase
movida acima):
function digaOi() { frase var; frase = "Olá"; alerta(frase); } digaOi();
…Ou mesmo assim (lembre-se, os blocos de código são ignorados):
function digaOi() { frase = "Olá"; // (*) se (falso) { frase var; } alerta(frase); } digaOi();
As pessoas também chamam esse comportamento de “hoisting” (aumentar), porque todos var
são “hoisted” (elevados) para o topo da função.
Portanto, no exemplo acima, if (false)
branch nunca é executado, mas isso não importa. A var
dentro dela é processada no início da função, portanto no momento de (*)
a variável existe.
As declarações são içadas, mas as atribuições não.
Isso é melhor demonstrado com um exemplo:
function digaOi() { alerta(frase); var frase = "Olá"; } digaOi();
A linha var phrase = "Hello"
possui duas ações:
Declaração de variável var
Atribuição de variável =
.
A declaração é processada no início da execução da função (“hoisted”), mas a atribuição funciona sempre no local onde aparece. Então o código funciona essencialmente assim:
function digaOi() { frase var; // a declaração funciona no início... alerta(frase); // indefinido frase = "Olá"; // ...atribuição - quando a execução chega até ela. } digaOi();
Como todas as declarações var
são processadas no início da função, podemos referenciá-las em qualquer lugar. Mas as variáveis são indefinidas até as atribuições.
Nos dois exemplos acima, alert
é executado sem erros, porque a variável phrase
existe. Mas seu valor ainda não foi atribuído, então mostra undefined
.
No passado, como existia apenas var
e não tinha visibilidade em nível de bloco, os programadores inventaram uma maneira de emulá-lo. O que eles fizeram foi chamado de “expressões de função invocadas imediatamente” (abreviadas como IIFE).
Isso não é algo que deveríamos usar hoje em dia, mas você pode encontrá-los em scripts antigos.
Um IIFE se parece com isto:
(função() { var mensagem = "Olá"; alerta(mensagem); // Olá })();
Aqui, uma expressão de função é criada e chamada imediatamente. Portanto, o código é executado imediatamente e possui suas próprias variáveis privadas.
A Expressão de Função é colocada entre parênteses (function {...})
, porque quando o mecanismo JavaScript encontra "function"
no código principal, ele a entende como o início de uma Declaração de Função. Mas uma declaração de função deve ter um nome, então esse tipo de código gerará um erro:
//Tenta declarar e chamar imediatamente uma função function() { // <-- SyntaxError: Instruções de função requerem um nome de função var mensagem = "Olá"; alerta(mensagem); // Olá }();
Mesmo se dissermos: “ok, vamos adicionar um nome”, isso não funcionará, pois o JavaScript não permite que declarações de função sejam chamadas imediatamente:
//erro de sintaxe por causa dos parênteses abaixo função ir() { }(); // <-- não é possível chamar a declaração de função imediatamente
Portanto, os parênteses ao redor da função são um truque para mostrar ao JavaScript que a função é criada no contexto de outra expressão e, portanto, é uma Expressão de Função: ela não precisa de nome e pode ser chamada imediatamente.
Existem outras maneiras além dos parênteses de dizer ao JavaScript que nos referimos a uma expressão de função:
// Maneiras de criar IIFE (função() { alert("Parênteses em torno da função"); })(); (função() { alert("Parênteses em torno de tudo"); }()); !função() { alert("O operador NOT bit a bit inicia a expressão"); }(); +função() { alert("Unário plus inicia a expressão"); }();
Em todos os casos acima, declaramos uma Expressão de Função e a executamos imediatamente. Notemos novamente: hoje em dia não há razão para escrever tal código.
Existem duas diferenças principais de var
em comparação com let/const
:
var
variáveis não têm escopo de bloco, sua visibilidade tem como escopo a função atual, ou global, se declarada fora da função.
as declarações var
são processadas no início da função (início do script para globais).
Há mais uma pequena diferença relacionada ao objeto global, que abordaremos no próximo capítulo.
Essas diferenças tornam var
pior do que let
na maioria das vezes. Variáveis em nível de bloco são ótimas. É por isso que let
foi introduzido no padrão há muito tempo e agora é uma forma importante (junto com const
) de declarar uma variável.