Escopo de variáveis globais e locais

Neste artigo, você aprenderá sobre o escopo e como ele funciona no JavaScript.

Escopo é um conceito fundamental em JavaScript e outras linguagens de programação que define o contexto em que as variáveis são acessadas e usadas. Ele se torna mais útil e aplicável ao seu código à medida que você continua aprendendo sobre JavaScript e trabalhando 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, não é necessário disponibilizá-la para o código que está sendo executado no momento.
  • 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áveis:por exemplo, você pode escrever uma função pura que não dependa do escopo externo. É possível mover essa função facilmente para outro lugar com mudanças mínimas.

O que é escopo?

O escopo de uma variável determina de onde dentro do código você pode usá-la.

O JavaScript define variáveis de escopo global ou local:

  • As variáveis com escopo global estão disponíveis em todos os outros escopos no código JavaScript.
  • Variáveis com escopo local estão disponíveis apenas 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, ela terá escopo local.

As próximas seções deste artigo discutem o bloco e o escopo lexical:

  • As variáveis de escopo de bloco estão disponíveis localmente para um bloco, conforme determinado pelo local das chaves em que a instrução de bloco é definida. Somente as variáveis declaradas com as palavras-chave let ou const têm escopo de bloqueio.
  • O escopo léxico usa o local em que uma variável é declarada no código-fonte para determinar onde ela está disponível. Use fechamentos para conceder a uma função incluída acesso às variáveis referenciadas no escopo externo conhecido como ambiente lexical.

Quando uma variável é acessada no escopo, o JavaScript retorna o valor atribuído ou produz um erro.

Para declarar uma variável:

  • Use as palavras-chave var, const ou let para declarar variáveis de escopo local ou 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 de inclusão 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 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.

Este exemplo demonstra o escopo local porque declara a variável greeting com a palavra-chave let em uma função. A variável greeting tem 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 dentro de um bloco para que ela só possa ser acessada 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 o escopo do 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. Portanto, só é possível acessar a variável greeting entre chaves, que especifica o escopo do bloco.

Este exemplo corrige o erro porque move o método console.log(message) 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 e a execução, é possível acessar o valor da variável globalMessage em qualquer lugar no programa JavaScript.

É possível ver 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á escopo global, e sim escopo do módulo. As variáveis com escopo de módulo estão disponíveis em qualquer parte 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, é preciso exportá-la do módulo em que ela foi criada e, em seguida, import do módulo que precisa acessar a variável.

Escopo local e escopo da função

Quando você cria variáveis em uma função JavaScript com as palavras-chave var, let ou const, elas são locais para a função, então só podem ser acessadas de dentro da função. As variáveis locais são criadas quando uma função é iniciada e são excluídas quando a execução da função termina.

Este exemplo declara a variável total na função addNumbers(). Você só pode 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 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 bloqueio

Blocos são usados para agrupar uma única instrução ou um conjunto de instruções. Você pode usar as palavras-chave const ou let para declarar uma variável local com escopo de bloco. Não é possível usar a palavra-chave var para declarar variáveis com escopo de bloqueio.

Por exemplo, neste bloco, o escopo da variável name e o valor "Elizabeth" estão dentro das chaves. As variáveis em um escopo de bloco não estão disponíveis fora do bloco.

{
    const name = "Elizabeth";
}

É possível usar variáveis com escopo de bloco nas instruções if, for ou while.

Observe as duas repetições for neste snippet de código. Uma repetição for usa a palavra-chave var para declarar a variável do inicializador, que aumenta com os números 0, 1 e 2. A outra repetição for usa a palavra-chave let para declarar a variável de inicializador.

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 na primeira repetição for vazou para fora da repetição for e ainda mantém um valor 2 porque a palavra-chave var não usa o escopo do bloco. O problema foi corrigido na segunda repetição 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.

Reutilizar o nome de uma variável em um escopo diferente

O escopo pode isolar uma variável dentro de uma função, mesmo quando você reutiliza o mesmo nome de variável em outro lugar em um escopo diferente.

Este exemplo mostra como usar o 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, portanto, não entram em conflito.

Fechamentos e escopo léxico

Encerramentos se referem a uma função fechada em que uma função interna pode acessar o escopo externo, também conhecido como ambiente lexical. Assim, em JavaScript, você usa fechamentos para permitir que as funções façam referência ao ambiente lexico externo, o que permite que o código dentro de uma função referencie variáveis declaradas fora dela. 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, seja chamada por outra função.

Neste exemplo, o código forma uma interdição com o ambiente léxico criado quando a função outer() é invocada, que se fecha 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 escopo lexical, o escopo é determinado durante a compilação do código-fonte, não no momento da execução. Para saber mais sobre o ambiente léxico, consulte Escopo lexical e fechamento.

Módulos

Os módulos JavaScript ajudam a organizar o código JavaScript. Usados corretamente, eles fornecem 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 de JavaScript deve entender. Para entender melhor o sistema de escopo, tente escrever seu próprio código com o JS Scope Visualizer (Visualizador de escopo JS). A demonstração usa cores no código para ajudar a visualizar escopos JavaScript.

Conclusão

Este artigo apresenta diferentes tipos de escopo. O escopo do JavaScript é um dos conceitos mais avançados no desenvolvimento da Web, então é ótimo que você tenha lido esse conteúdo e tendo dedicado tempo para entender esse assunto.

O escopo não é um recurso voltado para o usuário. Ela afeta apenas o desenvolvedor da Web que escreve o código, mas o conhecimento de como o escopo funciona pode ajudar você a corrigir bugs quando eles surgirem.