Neste artigo, você aprenderá sobre o escopo e como ele funciona no JavaScript.
O escopo é um conceito fundamental do JavaScript e de outras linguagens de programação que define o contexto em que as variáveis são acessadas e usadas. Ela se torna mais útil e aplicável ao código à medida que você aprende mais sobre JavaScript e trabalha mais com variáveis.
O escopo pode ajudar você a:
- Usar a memória com mais eficiência:o escopo permite carregar variáveis somente quando necessário. Se uma variável estiver fora do escopo, ela não precisa ser disponibilizada para o código em execução.
- Encontre e corrija bugs com mais facilidade: isolar variáveis com escopo local facilita a solução de bugs no seu código porque, ao contrário das variáveis globais, você pode confiar que o código de um escopo externo não pode manipular variáveis com escopo local.
- Crie pequenos blocos de código reutilizável:por exemplo, é possível escrever uma função pura que não depende de um escopo externo. Você pode facilmente mover essa função para outro lugar com alterações mínimas.
O que é escopo?
O escopo de uma variável determina de onde no código você pode usar uma variável.
O JavaScript define variáveis de escopo global ou local:
- Variáveis com escopo global estão disponíveis em todos os outros escopos no código JavaScript.
- As variáveis com escopo local estão disponíveis somente em um contexto local específico e são criadas por palavras-chave, como
var
,let
econst
. Se você usar as palavras-chavevar
,let
ouconst
para criar uma variável em uma função, essa variável terá escopo local.
As próximas seções deste artigo discutem o escopo de blocos e lexical:
- As variáveis do escopo do bloco ficam disponíveis localmente para um bloco, conforme determinado pelo local das chaves em que a instrução do bloco é definida. Somente variáveis declaradas com as palavras-chave
let
ouconst
têm escopo de bloco. - O escopo léxico usa o local onde uma variável é declarada no código-fonte para determinar onde ela está disponível. Você usa fechamentos para dar a uma função incluída acesso às variáveis referenciadas no escopo externo conhecido como ambiente léxico.
Quando uma variável é acessada dentro de seu escopo, o JavaScript retorna o valor atribuído a ela ou produz um erro.
Para declarar uma variável:
- Use as palavras-chave
var
,const
oulet
para declarar variáveis locais ou de escopo global. - Use as palavras-chave
const
oulet
para declarar variáveis de escopo de bloco.
Quando você declara uma variável var
em uma função, a declaração disponibiliza a variável para a função delimitadora mais próxima. Não é possível usar a palavra-chave var
para declarar variáveis com escopo de bloco.
Exemplos de escopo
Este exemplo demonstra o escopo global porque a variável greeting
é declarada fora de qualquer função ou bloco, o que disponibiliza o valor dela para todo o código no documento atual:
const greeting = 'hello';
console.log(greeting); // 'hello'
No exemplo de escopo global, a variável greeting
recebe um valor hello
.
Esse exemplo demonstra o escopo local porque declara a variável greeting
com a palavra-chave let
em uma função. A greeting
é uma variável com escopo local e não está disponível fora da função.
function greet() {
let greeting = 'Hello World!';
console.log(greeting);
}
Este exemplo demonstra o escopo do bloco porque declara a variável greeting
em um bloco para que a variável fique acessível somente dentro das chaves:
if (true) {
const greeting = 'hello';
}
console.log(greeting); // ReferenceError: greeting is not defined
Quando a função console.log
tenta gerar o valor da variável greeting
, o JavaScript retorna uma mensagem de erro ReferenceError
em vez da mensagem hello
esperada. Por quê?
Um erro é retornado porque a variável greeting
tem um escopo de bloco, e o bloco mais próximo faz parte da instrução condicional if
. Não é possível acessar as variáveis let
e const
declaradas dentro de um bloco de fora dele. Assim, só é possível acessar a variável greeting
entre chaves, que especifica o escopo do bloco.
Este exemplo corrige o erro porque ele move o método console.log(message)
para dentro das chaves. O código atualizado realoca o método console.log(message)
dentro do bloco.
if (true) {
const greeting = 'hello';
console.log(greeting);
}
Tipos de escopo
Escopo global
É possível acessar variáveis com escopo global de qualquer lugar no programa.
Considere um arquivo HTML que importa dois arquivos JavaScript: file-1.js
e file-2.js
:
<script src="file-1.js"></script>
<script src="file-2.js"></script>
Neste exemplo, a variável globalMessage
tem um escopo global e é escrita fora de uma função. Durante a execução, é possível acessar o valor da variável globalMessage
de qualquer lugar no programa JavaScript.
Confira o conteúdo dos arquivos file-1.js
e file-2.js
neste snippet de código. Observe a disponibilidade da variável globalMessage
nos dois arquivos.
// file-1.js
function hello() {
var localMessage = 'Hello!';
}
var globalMessage = 'Hey there!';
// file-2.js
console.log(localMessage); // localMessage is not defined
console.log(globalMessage); // Hey there!
Há outro tipo de escopo que não é muito discutido neste artigo. Se você criar uma variável dentro de um módulo JavaScript, mas fora de uma função ou bloco, ela não terá um escopo global, mas sim um escopo de módulo. As variáveis com escopo de módulo estão disponíveis em qualquer lugar do módulo atual, mas não em outros arquivos ou módulos. Para disponibilizar uma variável com escopo de módulo para outros arquivos, você precisa exportá-la do módulo em que foi criada e depois import do módulo que precisa acessar a variável.
Escopo local e escopo de função
Quando você cria variáveis em uma função JavaScript com as palavras-chave var
, let
ou const
, as variáveis são locais em relação à função. Assim, elas só podem ser acessadas dentro da função. As variáveis locais são criadas quando uma função é iniciada e são efetivamente excluídas quando a execução da função termina.
Este exemplo declara a variável total
na função addNumbers()
. Só é possível acessar as variáveis a
, b,
e total
na função addNumbers()
.
function addNumbers(a, b) {
const total = a + b;
}
addNumbers(3, 4);
É possível usar as palavras-chave let
e const
para nomear as variáveis. Quando você usa a palavra-chave let
, o JavaScript pode atualizar a variável. No entanto, com a palavra-chave const
, a variável permanece constante.
var variable1 = 'Declared with var';
var variable1 = 'Redeclared with var';
variable1; // Redeclared with var
let variable2 = 'Declared with let. Cannot be redeclared.';
variable2 = 'let cannot be redeclared, but can be updated';
variable2; // let cannot be redeclared, but can be updated
const variable3 = 'Declared with const. Cannot be redeclared or updated';
variable3; // Declared with const. Cannot be redeclared or updated
Escopo do bloco
Os blocos são usados para agrupar uma ou várias instruções. É possível usar as palavras-chave const
ou let
para declarar uma variável local de escopo de bloco. Não é possível usar a palavra-chave var
para declarar variáveis com escopo de bloco.
Por exemplo, neste bloco, o escopo da variável name
e o valor "Elizabeth"
dela estão dentro das chaves. As variáveis dentro de um escopo de bloco não estão disponíveis fora desse bloco.
{
const name = "Elizabeth";
}
É possível usar variáveis com escopo de bloco nas instruções if
, for
ou while
.
Observe os dois loops for
neste snippet de código. Uma repetição for
usa a palavra-chave var
para declarar a variável do inicializador, que incrementa os números 0
, 1
e 2
. A outra repetição for
usa a palavra-chave let
para declarar a variável de inicialização.
for (var i = 0; i < 2; i++) {
// ...
}
console.log(i); // 2
for (let j = 0; j < 2; j++) {
// ...
}
console.log(j); // The j variable isn't defined.
No exemplo de código anterior, a variável i
no primeiro loop for
vazou para fora do loop for
e ainda retém um valor 2
porque a palavra-chave var
não usa o escopo de bloco. O problema foi corrigido no segundo loop de for
, em que a variável j
declarada com a palavra-chave let
tem o escopo definido para o bloco da repetição for
e não existe depois que a repetição for
é concluída.
Reutilização de um nome de variável em um escopo diferente
O escopo pode isolar uma variável em uma função, mesmo quando você reutilizar o mesmo nome de variável em outro escopo de um escopo diferente.
Este exemplo mostra como o uso do escopo permite reutilizar o mesmo nome de variável em funções diferentes:
function listOne() {
let listItems = 10;
console.log(listItems); // 10
}
function listTwo() {
let listItems = 20;
console.log(listItems); // 20
}
listOne();
listTwo();
As variáveis listItems
nas funções listOne()
e listTwo()
recebem os valores esperados e, por isso, não entram em conflito entre si.
Fechamentos e escopo lexical
Fechamentos se referem a uma função incluída em que uma função interna pode acessar o escopo da função externa, que também é conhecido como ambiente léxico. Assim, em JavaScript, você usa fechamentos para permitir que as funções façam referência ao ambiente léxico externo, o que permite que o código dentro de uma função referencie variáveis declaradas fora da função. Na verdade, é possível codificar uma cadeia de referências a ambientes léxicos externos para que uma função seja chamada por outra que, por sua vez, será chamada por outra.
Neste exemplo, o código forma um fechamento com o ambiente léxico criado quando a função outer()
é invocada, que é fechada sobre a variável hello
. Assim, a variável hello
é usada na função de callback setTimeout
.
function outer() {
const hello = 'world';
setTimeout(function () {
console.log('Within the closure!', hello)
}, 100);
}
outer();
Com o escopo léxico, o escopo é determinado durante a compilação do código-fonte, não durante a execução. Para saber mais sobre o ambiente léxico, consulte Escopo e fechamento léxico.
Módulos
Os módulos JavaScript ajudam a organizar o código JavaScript. Usadas corretamente, elas oferecem uma estrutura eficaz para a base de código e ajudam na reutilização do código. Em vez de usar variáveis globais para compartilhar variáveis em arquivos diferentes, os módulos JavaScript oferecem uma técnica para exportar e import variáveis.
// hello.js file
function hello() {
return 'Hello world!';
}
export { hello };
// app.js file
import { hello } from './hello.js';
console.log(hello()); // Hello world!
Demonstração do visualizador de escopo
O escopo é um conceito fundamental que todo desenvolvedor JavaScript precisa entender. Para entender melhor o sistema de escopo, tente escrever seu próprio código com o JS Scope Visualizer. A demonstração usa cores no código para ajudar você a visualizar os escopos do JavaScript.
Conclusão
Este artigo apresenta diferentes tipos de escopo. O escopo do JavaScript é um dos conceitos mais avançados no desenvolvimento da Web, por isso é ótimo que você tenha lido o conteúdo e tenha dedicado um tempo para entendê-lo.
O escopo não é um recurso voltado para o usuário. Ele afeta apenas o desenvolvedor Web que escreve o código, mas o conhecimento de como o escopo funciona pode ajudar você a corrigir bugs quando eles surgirem.