SkillsTecnológicas
Menu
Conteúdo da trilha

Escopo de variáveis

Entenda onde cada variável pode ser acessada, como nomes são resolvidos entre ambientes e por que limitar o escopo reduz dependências e erros.

Conteúdo 38 de 39

Três ambientes de madeira desenhados a lápis mostram a busca por um valor externo e o bloqueio de um valor restrito ao espaço interno

Escopo define onde um nome pode ser usado

O escopo de uma variável é a região do programa na qual seu nome pode ser encontrado e associado à declaração correta. Fora dessa região, usar o mesmo nome pode causar erro, encontrar outra declaração ou ter um significado diferente, conforme a linguagem.

Considere este exemplo em JavaScript:

const moeda = "BRL";

function calcularTotal(subtotal) {
  const taxa = 0.10;
  const total = subtotal + subtotal * taxa;
  return total;
}

const resultado = calcularTotal(200);
console.log(resultado); // 220

Há três papéis diferentes:

  • moeda está declarada fora da função e pertence ao ambiente mais externo desse exemplo;
  • subtotal é um parâmetro local criado para cada chamada;
  • taxa e total são nomes locais da função.

O código externo pode usar resultado, mas não pode acessar diretamente taxa ou total. Esses nomes só existem para o corpo de calcularTotal. A função, por sua vez, pode consultar nomes de ambientes externos, como moeda, caso não encontre uma declaração local com o mesmo nome.

Essa é uma diferença importante em relação a variáveis e constantes: declarar e atualizar explicam o estado armazenado; escopo explica onde o nome desse estado é visível.

Procure do ambiente mais próximo para fora

Quando encontra um identificador, uma linguagem com escopo léxico normalmente procura primeiro no ambiente atual. Se o nome não estiver ali, examina ambientes que envolvem aquele trecho, do mais próximo para o mais distante, seguindo as regras da linguagem.

const moeda = "BRL";

function formatarPreco(valor) {
  const separador = ",";

  if (valor >= 1000) {
    const prefixo = "A partir de";
    return `${prefixo} ${moeda} ${valor}${separador}00`;
  }

  return `${moeda} ${valor}${separador}00`;
}

Dentro do bloco do if, a resolução encontra:

  1. prefixo no próprio bloco;
  2. valor e separador no escopo da função;
  3. moeda no escopo externo.

Fora do bloco, prefixo não está disponível. Fora da função, valor e separador também não estão. O acesso se propaga dos ambientes internos para os externos; uma região externa não ganha acesso automático aos nomes exclusivos de uma região interna.

Mapa com módulo, função e bloco condicional aninhados; a busca parte do bloco e encontra desconto local, subtotal na função e moeda no módulo
Para resolver um nome, comece no ponto de uso e caminhe pelos ambientes visíveis, do mais próximo para fora.

Esse modelo permite diagnosticar uma referência sem adivinhar: marque o ponto de uso, liste os ambientes que o envolvem e procure a primeira declaração compatível.

Parâmetros e variáveis da função são locais à chamada

Cada execução de uma função cria seu próprio conjunto de parâmetros e variáveis locais. Duas chamadas podem usar os mesmos nomes sem compartilhar automaticamente os mesmos valores:

function aplicarDesconto(subtotal, percentual) {
  const desconto = subtotal * (percentual / 100);
  return subtotal - desconto;
}

const totalA = aplicarDesconto(200, 10); // desconto local: 20
const totalB = aplicarDesconto(80, 5);   // desconto local: 4

O desconto da primeira chamada não é reutilizado pela segunda. Cada chamada calcula um valor local independente. Depois que a função termina, seu nome local deixa de estar acessível pelo código chamador; somente o valor entregue por retorno atravessa explicitamente essa fronteira.

Esse isolamento ajuda a reutilização. O chamador informa as dependências pelos argumentos, a função trabalha com estado local e devolve um resultado. Não é necessário criar nomes globais diferentes para cada compra.

Blocos podem criar fronteiras menores

Um bloco é uma região delimitada por chaves, indentação ou palavras de início e fim. Entretanto, bloco visual e escopo de variável não são universais.

Em JavaScript, declarações com let e const pertencem ao bloco que as contém:

if (pedidoAprovado) {
  const mensagem = "Pedido aprovado";
  console.log(mensagem); // disponível
}

console.log(mensagem); // ReferenceError

Em C#, uma variável local declarada em um bloco também fica limitada a esse bloco. Em Python, por outro lado, blocos de if, for e while não criam um novo escopo local de função: uma atribuição feita neles pertence ao bloco de código da função. Funções, módulos e classes participam da resolução de nomes segundo regras próprias.

ConstruçãoJavaScript com let/constC#Python
funçãocria escopo localcria espaço localcria escopo local
bloco ifcria escopo de blocolimita variáveis locais declaradas nelenão cria novo escopo local de função
laçolet/const seguem o blocovariável local segue a construçãoalvo do laço pertence ao escopo da função

A tabela serve como orientação, não como substituto da documentação. Ao trocar de linguagem, confirme quais construções realmente introduzem uma fronteira de escopo.

Nomes iguais podem provocar sombreamento

Quando um ambiente interno declara um nome já existente no ambiente externo, a declaração mais próxima pode sombrear a outra. Em JavaScript:

const saldo = 100;

{
  const saldo = 50;
  console.log(saldo); // 50
}

console.log(saldo); // 100

O bloco não alterou o primeiro saldo. Ele criou outra ligação com o mesmo nome. Dentro do bloco, a busca encontra saldo = 50 primeiro; depois do bloco, a declaração externa volta a ser a visível.

Um saldo externo de 100 é sombreado dentro de um bloco por outro saldo de 50; fora do bloco, o nome volta a representar 100
Sombreamento muda qual declaração um nome resolve; não significa que o valor externo foi atualizado.

As permissões variam. JavaScript aceita esse exemplo com const em blocos aninhados. Python decide os nomes locais de uma função ao analisar as operações de ligação presentes no bloco inteiro, o que pode gerar UnboundLocalError quando um nome é lido antes da atribuição local. C# restringe determinadas redeclarações de variáveis locais em espaços aninhados.

Mesmo quando permitido, sombreamento pode confundir. Nomes como total, status e resultado repetidos em muitos níveis tornam difícil saber qual dado uma linha utiliza. Mantenha o mesmo nome quando a distinção estiver evidente e curta; renomeie quando os conceitos forem diferentes, como subtotal e totalComFrete.

Escopo e tempo de vida respondem a perguntas diferentes

Escopo responde: em qual trecho posso escrever este nome? Tempo de vida responde: por quanto tempo o valor ou objeto permanece disponível na execução?

Na maioria dos exemplos simples, uma variável local só pode ser referenciada dentro da função e deixa de ser necessária quando a chamada termina. Mas as duas ideias não são idênticas. Uma função interna pode capturar uma ligação do ambiente em que foi criada:

function criarContador() {
  let valor = 0;

  return function incrementar() {
    valor += 1;
    return valor;
  };
}

const proximo = criarContador();
console.log(proximo()); // 1
console.log(proximo()); // 2

O código externo continua sem poder usar o nome valor diretamente. Ainda assim, a função retornada conserva acesso à ligação por meio de um closure. O exemplo demonstra por que “saiu do bloco” não significa necessariamente “o objeto já foi destruído”. O funcionamento aprofundado de closures fica para estudos posteriores; aqui, guarde apenas a separação entre visibilidade do nome e duração do estado.

Variáveis globais criam dependências que não aparecem na chamada

Um nome global pode ser conveniente para uma constante compartilhada ou uma configuração controlada. O problema aparece quando funções leem e alteram estado global mutável sem deixar essa dependência explícita:

let taxa = 0.10;

function calcularTotal(subtotal) {
  return subtotal + subtotal * taxa;
}

A chamada calcularTotal(200) parece depender apenas de 200, mas seu resultado também depende do valor atual de taxa. Outro trecho pode mudar a variável antes da chamada, fazendo o mesmo argumento produzir outra resposta.

Uma interface explícita facilita leitura e teste:

function calcularTotal(subtotal, taxa) {
  return subtotal + subtotal * taxa;
}

const total = calcularTotal(200, 0.10); // 220
Comparação entre calcularTotal lendo uma taxa global ausente da chamada e calcularTotal recebendo subtotal e taxa explicitamente e retornando 220
Parâmetros e retornos tornam o fluxo de dados visível; estado global mutável pode esconder uma entrada real da função.

Isso não transforma todo nome externo em erro. Constantes imutáveis de módulo, serviços deliberadamente compartilhados e configurações possuem usos legítimos. A pergunta prática é: “se este valor mudar, quais partes do programa mudam de comportamento?”. Quanto mais difícil for responder, maior é o custo do alcance amplo.

Prefira o menor escopo que atende ao uso real

Uma variável deve ser declarada perto do primeiro uso e permanecer visível apenas pelo tempo necessário para cumprir sua responsabilidade. Um alcance menor:

  • reduz a quantidade de lugares capazes de ler ou alterar o estado;
  • evita colisões entre nomes genéricos;
  • deixa dependências mais fáceis de identificar;
  • simplifica testes e mudanças;
  • reduz o estado que o leitor precisa manter mentalmente.

Não mova toda declaração para o bloco mais interno sem avaliar o fluxo. Se um resultado calculado em uma condição precisa ser usado depois, talvez a variável pertença ao escopo externo comum, ou talvez cada caminho possa retornar seu resultado diretamente. A escolha deve refletir onde o dado é realmente necessário.

Ao revisar uma variável, use este roteiro:

  1. marque onde ela é declarada;
  2. liste todos os pontos de leitura e alteração;
  3. identifique a menor região que contém esses usos;
  4. verifique se existe outro nome igual em ambiente aninhado;
  5. transforme dependências externas mutáveis em parâmetros quando isso esclarecer o contrato;
  6. confirme as regras da linguagem para função, bloco, módulo e laço.

Faça um teste de visibilidade

Analise o exemplo:

const percentual = 10;

function calcular(preco) {
  const subtotal = preco * 2;

  if (subtotal >= 100) {
    const percentual = 20;
    const desconto = subtotal * (percentual / 100);
    return subtotal - desconto;
  }

  return subtotal;
}

Sem executar o código, responda:

  1. qual percentual é encontrado dentro do if?
  2. o percentual global foi alterado?
  3. desconto pode ser lido depois do bloco?
  4. subtotal pode ser usado em ambos os retornos?
  5. o chamador consegue acessar subtotal diretamente?
  6. quais resultados surgem para calcular(60) e calcular(30)?

As respostas são: o percentual local 20; o global continua 10; desconto fica restrito ao bloco; subtotal pertence à função e está disponível nos dois caminhos; o chamador recebe apenas o retorno; e os resultados são 96 e 60.

Depois, renomeie o valor interno para percentualPromocional. O comportamento não muda, mas a intenção fica mais fácil de seguir. Esse contraste mostra que um programa pode estar correto para a linguagem e ainda ser desnecessariamente difícil para pessoas.

O que você deve guardar

Escopo é a região em que um nome está visível. Para resolver uma referência, comece no ponto de uso e procure nos ambientes permitidos, do mais próximo para fora. Parâmetros e variáveis locais isolam cada chamada; blocos podem criar fronteiras adicionais, dependendo da linguagem.

Sombreamento cria declarações distintas com o mesmo nome, escopo não é sinônimo de tempo de vida e estado global mutável pode esconder dependências. Prefira o menor alcance que cubra os usos reais e torne entradas e saídas explícitas. Com funções, parâmetros, retornos e escopo organizados, veja como cada chamada mantém seus próprios valores em introdução à recursividade.

Referências