Escopo de variáveis globais e locais

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 e const. Se você usar as palavras-chave var, let ou const 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 ou const 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 ou let para declarar variáveis locais ou de escopo global.
  • Use as palavras-chave const ou let 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.