Pular para o conteúdo
murad.guide

Introdução Completa à Web

Capítulo 16

Funções e escopo

6 min de leitura 1.038 palavras 2 gravuras

Uma função é um pedaço reutilizável de código, como uma classe CSS que você aplica em vários lugares. O escopo é onde um nome vale. Neste capítulo a Casa da Restinga empacota um cumprimento e aprende o que some quando o bloco acaba.

O exemplo está em exemplos/16-funcoes-escopo/. Natal, fictício. Este volume tem 23 capítulos. Console com F12.

Trabalhe na cópia inicio da etapa. Leia e digite o código; mude uma coisa. Antes de recarregar, anote sua previsão. Depois confira com a referência completa. Se nada mudar, confira o arquivo salvo e o caminho aberto. Blocos adicionais do capítulo podem ser digitados no console; o exemplo completo mostra a aplicação daquela etapa.

O que você vai aprender

Um bloco com nome

function maisDois(numero) {
  return numero + 2;
}

const resposta = maisDois(5);
console.log(resposta);

function maisDois declara. numero é o parâmetro: o apelido do valor que entra. return devolve o resultado para quem chamou.

maisDois(5) chama. O 5 entra no lugar de numero. resposta guarda 7.

Sem return, a função faz o trabalho e devolve undefined.

Mude uma coisa. Chame maisDois(10) em vez de 5. Preveja 12. Recarregue e confira.

Erro comum: escrever maisDois sem (). Sem parênteses, você não chama: só aponta para a função.

Vários parâmetros, uma ordem

function cumprimentar(primeiro, ultimo, titulo, saudacao) {
  return `${saudacao} ${titulo} ${ultimo}! Que bom te ver, ${primeiro}.`;
}

console.log(cumprimentar("Iara", "Costa", "chef", "Olá"));
console.log(cumprimentar("Tomás", "Vale", "barista", "Bem-vindo"));

A ordem da chamada é a ordem da lista. Trocar Iara e Costa de lugar muda o sentido da frase.

Quantos parâmetros você quiser. Zero também vale: function abrirPorta() { ... }.

Os textos são fictícios. Não coloque o nome real da turma neste arquivo.

Mude uma coisa. Inverta "Iara" e "Costa" na primeira chamada. Preveja “Olá chef Iara”. Recarregue e confira. Depois restaure.

Erro comum: achar que o nome do parâmetro tem de ser igual ao nome de fora. primeiro recebe o valor, não o nome da variável de fora.

A chamada usa nomes de fora

const cidade = "Natal";
const bairro = "Ponta Negra";
const pais = "Brasil";

function contarOrigem(cidadeCasa, estadoCasa, paisCasa) {
  console.log(`Você fala da ${cidadeCasa}, ${estadoCasa}, ${paisCasa}.`);
}

contarOrigem(cidade, bairro, pais);

Quem chama passa os valores. Quem recebe usa os apelidos internos. cidade lá fora e cidadeCasa lá dentro são nomes diferentes para o mesmo texto nesta chamada.

Parênteses depois de um nome = chamada de função. Isso vira um reflexo de leitura.

Mude uma coisa. Passe "Rocas" no segundo argumento. Preveja Ponta Negra sair da frase. Recarregue e confira.

Erro comum: contarOrigem com um argumento só. Os outros parâmetros ficam undefined e a frase esburaca.

Função com parâmetro que entra e valor que sai; escopo dentro das chaves

Três jeitos, o mesmo ofício

function latir() {
  console.log("au");
}

const miar = function () {
  console.log("miau");
};

const piar = () => {
  console.log("piu piu");
};

latir();
miar();
piar();

A primeira é a declaração clássica. A segunda guarda uma função num const. A terceira é a seta => (um = colado num >).

Há diferenças finas. Neste volume, trate as três como “isso é função”. Prefira function nome() enquanto estiver aprendendo: o nome aparece no erro do console.

Mude uma coisa. Acrescente latir(); de novo no fim. Preveja dois au. Recarregue e confira.

Erro comum: const piar = > { com espaço quebrando a seta. É =>, junto.

Escopo da função

Cada chamada tem um espaço de trabalho. O que você declara dentro não existe fora depois que a função termina.

function maisCinco(numero) {
  const segredo = "só aqui dentro";
  return numero + 5;
}

maisCinco(10);
console.log(segredo);

A última linha falha: segredo is not defined. O nome viveu entre as { } da função e foi descartado.

return é o jeito honesto de entregar um valor para fora. Sem return (e sem guardar o retorno), o trabalho some.

Mude uma coisa. Troque console.log(segredo) por console.log(maisCinco(10)). Preveja 15. Recarregue e confira.

Erro comum: declarar const total dentro da função e tentar usar total no HTML da página. O HTML não vê o nome. Só o valor devolvido, se você o escrever depois no DOM (capítulo futura).

Escopo do bloco

if, for e while também abrem { }. let e const declarados ali morrem no fim do bloco.

let mesa = 0;
for (let i = 0; i <= 10; i++) {
  mesa++;
}
console.log(mesa);
console.log(i);

mesa foi declarada fora: sobrevive. i foi declarada no for: a última linha falha.

Um jeito imperfeito, mas útil agora: um nome vive da { mais próxima até a } que a fecha. Importa onde o nome é declarado, não onde ele é alterado.

Mude uma coisa. Declare let i = 0 antes do for e use for (; i <= 10; i++). Preveja console.log(i) funcionar. Recarregue. (Não é o estilo preferido; só para ver o escopo.)

Erro comum: achar que “já imprimiu i dentro do laço, então existe fora”. Imprimir não exporta o nome.

Fora vive; dentro some

const A = "A";
let F;

function fazer(B) {
  const C = "C";
  let H = "H";
  if (1 + 1 === 2) {
    const D = "D";
    H = "outra coisa";
  }
  console.log(H);
  F = "F";
}

fazer("B");
console.log(A);
console.log(F);

A é de fora: fazer pode ler. F foi declarado fora e preenchido dentro: sobrevive. B e C morrem no fim da chamada. D morre no fim do if. H foi declarado na função, fora do if: a troca para "outra coisa" vale até a função acabar.

Leia o exemplo completo em exemplos/16-funcoes-escopo/ e marque, no papel, o que imprime e o que quebra. Depois confira as respostas.

Arquivo no disco: o .js. Console no navegador: o que ainda está vivo. 127.0.0.1 se estiver servindo. Publicação não é pedida.

Mude uma coisa. Acrescente console.log(C) depois de fazer("B"). Preveja erro. Recarregue e confira.

Erro comum: console.log no meio da função “para sempre”. Quando a função acaba, aqueles nomes internos não ficam gravados na página.

Início da referência desta etapa no navegador local

Atividades

  1. O que os parênteses fazem em maisDois(5) que maisDois sozinho não faz?
  2. Por que a ordem dos argumentos em cumprimentar importa?
  3. Por que console.log(segredo) falha depois de maisCinco(10)?
  4. i de um for (let i = 0; ...) existe depois do laço?

Respostas comentadas

  1. Chamam a função e passam o 5. Sem (), o nome só aponta para a função.
  2. Porque cada posição preenche um parâmetro. O primeiro texto vai para primeiro, o segundo para ultimo.
  3. segredo foi declarado dentro da função. Ao terminar, o nome é descartado. O return entrega só o número.
  4. Não. let i vive no bloco do for. Depois, i is not defined. A e F no exemplo global imprimem; C e D não.

Referências

O arquivo completo

Referência do exemplo 16-funcoes-escopo, reproduzida aqui para que você possa conferir sem sair da página.

experiments.js

function maisDois(numero) {
  return numero + 2;
}
console.log(maisDois(5));
function maisCinco(numero) {
  const segredo = "só aqui dentro";
  return numero + 5;
}
console.log(maisCinco(10));

assets/stylesheets/main.css

*,
*::before,
*::after { box-sizing: border-box; }
html, body, h1, h2, h3, p, ul { margin: 0; padding: 0; }
img { max-width: 100%; height: auto; }
body {
  font-family: "Segoe UI", Tahoma, sans-serif;
  line-height: 1.5;
  color: #1a2a2c;
  background: #f7f3ea;
}
a { color: #0d5c63; }
a:focus-visible, button:focus-visible, input:focus-visible {
  outline: 3px solid #c45c26;
  outline-offset: 3px;
}
.skip { position: absolute; left: -999px; }
.skip:focus { left: 0.5rem; top: 0.5rem; background: #fff; padding: 0.4rem 0.7rem; z-index: 2; }
.wrap { width: min(44rem, calc(100% - 2rem)); margin-inline: auto; }
header { background: #0d5c63; color: #fff; padding: 1rem 0; }
header a { color: #fff; }
.brand { font-weight: 700; text-decoration: none; }
nav ul { list-style: none; display: flex; gap: 1rem; margin-top: 0.5rem; }
nav a[aria-current="page"] { text-decoration: underline; text-underline-offset: 0.2em; }
main { padding: 1.5rem 0 3rem; }
footer { border-top: 1px solid #ddd4c4; padding: 1rem 0; font-size: 0.95rem; }
article { margin: 1.25rem 0; padding-bottom: 1rem; border-bottom: 1px solid #ddd4c4; }