Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Decisões com condicionais

Condicionais fazem o programa escolher um caminho. O if executa um bloco só quando a condição entre parênteses é verdadeira; o else cobre o caso contrário, e o else if encadeia novas condições. Elas são testadas de cima para baixo e apenas o primeiro bloco verdadeiro roda, por isso a ordem das faixas importa.

JavaScript Fundamentos Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
JavaScript / Fundamentos

Decisões com condicionais

por Briar Westbrook
Iniciante

Entenda o conceito

Condicionais fazem o programa escolher um caminho. O if executa um bloco só quando a condição entre parênteses é verdadeira; o else cobre o caso contrário, e o else if encadeia novas condições. Elas são testadas de cima para baixo e apenas o primeiro bloco verdadeiro roda, por isso a ordem das faixas importa.

As condições costumam vir de comparações (===, !==, <, >=) combinadas com operadores lógicos: && (e) exige que tudo seja verdadeiro, || (ou) basta que uma parte seja, e ! inverte o resultado. Prefira sempre === e !==, que comparam valor e tipo, ao ==, que converte os tipos antes de comparar.

Quando um mesmo valor é comparado com várias opções fixas, o switch fica mais legível que uma fila de else if; cada case termina com break. Para escolher entre dois valores numa expressão curta, existe o operador ternário condição ? a : b. E todo valor tem um lado verdadeiro ou falso: 0, '', null, undefined, NaN e false são falsy, o resto é truthy. O operador ?? dá um valor padrão só para null e undefined.

↳ Pontos-chave
  • Numa cadeia if / else if / else, só o primeiro bloco verdadeiro é executado; ordene as faixas do limite mais alto para o mais baixo (ou o contrário, com cuidado).
  • Use === e !== nas comparações: 0 == '' dá true, mas 0 === '' dá false.
  • && exige todas as condições, || exige pelo menos uma e ! inverte um boolean.
  • No switch, esquecer o break faz a execução seguir para o próximo case; default trata os valores que não casaram com nenhum.
  • 0, '', null, undefined, NaN e false são falsy; para valor padrão sem perder o 0, use ?? em vez de ||.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01Faixas de frete da loja com if, else if e else

frete.js
// Frete da loja decidido por faixas de valor com if, else if e else.

const subtotal = 149.9;
const cidade = 'Campinas';

console.log(`Subtotal do carrinho: R$ ${subtotal.toFixed(2)}`);

// As condições são testadas de cima para baixo; só o primeiro bloco verdadeiro roda
let frete;
if (subtotal >= 200) {
  frete = 0;
} else if (subtotal >= 100) {
  frete = 9.9;
} else {
  frete = 19.9;
}
console.log(`Frete: R$ ${frete.toFixed(2)}`);

// Um if sozinho, sem else: só faz algo quando a condição é verdadeira
if (frete === 0) {
  console.log('Parabéns, você ganhou frete grátis!');
}

// Quanto falta para a próxima faixa, calculado só quando faz sentido
if (subtotal < 200) {
  const falta = 200 - subtotal;
  console.log(`Faltam R$ ${falta.toFixed(2)} para o frete grátis.`);
}

// Comparação de texto com === diferencia maiúsculas de minúsculas
if (cidade === 'Campinas') {
  console.log('Entrega no mesmo dia disponível.');
} else {
  console.log('Entrega em até 3 dias úteis.');
}

A cadeia if / else if / else define o frete por faixas. Como as condições são testadas de cima para baixo, um subtotal de 149.9 falha em >= 200, passa em >= 100 e para ali: o else final nem é avaliado. Por isso a faixa mais alta vem primeiro; se >= 100 estivesse no topo, uma compra de R$ 250 também cairia nela.

Os if seguintes não têm else: eles só imprimem algo quando a condição é verdadeira. O último compara textos com ===, que exige igualdade exata, inclusive de maiúsculas e minúsculas.

02Regras de empréstimo da biblioteca com switch e operadores lógicos

emprestimo.js
// Regras de empréstimo da biblioteca com operadores lógicos e switch.

const tipoLeitor = 'professor';
const livrosEmMaos = 2;
const temAtraso = false;
const livroDeConsulta = false;

// switch compara o valor com cada case usando ===
let limite;
let prazoDias;
switch (tipoLeitor) {
  case 'aluno':
    limite = 3;
    prazoDias = 7;
    break; // sem o break, o código continuaria no próximo case
  case 'professor':
    limite = 5;
    prazoDias = 15;
    break;
  default:
    limite = 1;
    prazoDias = 5;
}
console.log(`Leitor: ${tipoLeitor} | limite: ${limite} | prazo: ${prazoDias} dias`);

// && exige que todas as condições sejam verdadeiras; ! inverte um boolean
const podeEmprestar = !temAtraso && livrosEmMaos < limite && !livroDeConsulta;
console.log('Pode emprestar?', podeEmprestar);

if (podeEmprestar) {
  console.log(`Empréstimo liberado. Devolva em ${prazoDias} dias.`);
} else if (temAtraso || livroDeConsulta) {
  // || basta que uma das condições seja verdadeira
  console.log('Empréstimo negado: atraso pendente ou livro só para consulta.');
} else {
  console.log('Empréstimo negado: limite de livros atingido.');
}

O switch compara tipoLeitor com cada case usando === e define limite e prazo de uma vez. O break encerra o switch; sem ele, o código do próximo case também rodaria. O default cobre qualquer tipo de leitor não previsto, como um visitante.

A constante podeEmprestar junta três condições com &&: o leitor não pode ter atraso (!temAtraso), precisa estar abaixo do limite e o livro não pode ser de consulta. No else if, o || basta que um dos impedimentos seja verdadeiro para explicar a recusa.

03Ternário, truthy/falsy e ?? numa receita

receita.js
// Ternário, valores truthy/falsy e ?? ao montar uma receita.

const receita = 'Panqueca';
const porcoesPedidas = 0; // o usuário digitou zero de propósito
const observacao = ''; // campo deixado em branco
let tempoPreparo; // ainda não informado

// O ternário escolhe entre dois valores: condição ? seVerdadeiro : seFalso
const rotulo = porcoesPedidas === 1 ? 'porção' : 'porções';
console.log(`${receita}: ${porcoesPedidas} ${rotulo}`);

// 0, '', null, undefined, NaN e false são falsy: um if os trata como falso
if (observacao) {
  console.log('Observação:', observacao);
} else {
  console.log('Sem observações.');
}

// || troca qualquer valor falsy, inclusive o 0 que foi digitado de propósito
const porcoesComOu = porcoesPedidas || 4;
// ?? troca só null e undefined, preservando o 0
const porcoesComNulo = porcoesPedidas ?? 4;
console.log('Com ||:', porcoesComOu, '| com ??:', porcoesComNulo);

// ?? é o jeito certo de dar um valor padrão a algo ainda não informado
const tempo = tempoPreparo ?? 20;
console.log(`Tempo de preparo: ${tempo} minutos`);

// Ternário dentro do template literal para uma mensagem curta
const dificuldade = tempo > 30 ? 'trabalhosa' : 'rápida';
console.log(`Receita ${dificuldade} para o lanche.`);

O ternário escolhe entre 'porção' e 'porções' numa única expressão, sem precisar de let e de um if completo. Em seguida, if (observacao) usa a string diretamente como condição: a string vazia é falsy, então o else é executado.

A comparação entre || e ?? é o ponto principal. Como 0 é falsy, porcoesPedidas || 4 descarta o zero digitado e devolve 4; já ?? só troca null e undefined, então preserva o 0. Para tempoPreparo, que ainda é undefined, o ?? aplica o padrão de 20 minutos.


Agora é com você

Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.

01Conceito no boletimFácil

A aluna Bia terminou o bimestre com média 8.6. Converta a média em conceito (A a partir de 9, B a partir de 7, C a partir de 5 e D abaixo disso) e informe se ela está aprovada (média a partir de 6) ou em recuperação.

↳ Requisitos
  • Calcular o conceito com uma única cadeia if / else if / else.
  • Escolher a situação ('aprovada' ou 'em recuperação') com o operador ternário.
  • Imprimir aluna, média, conceito e situação em linhas separadas.
  • A saída deve ser idêntica à saída esperada.
código inicial
const aluna = 'Bia';
const media = 8.6;

let conceito;
// escolha o conceito com if / else if / else

// escolha a situação com o ternário e imprima o boletim
saída esperada
Aluna: Bia
Média: 8.6
Conceito: B
Situação: aprovada
Ver dica

Comece testando o limite mais alto: se a média for pelo menos 9, nenhuma outra faixa precisa ser olhada.

Ver resposta
// Conceito e situação de uma aluna a partir da média final
const aluna = 'Bia';
const media = 8.6;

// Do maior limite para o menor: a primeira condição verdadeira decide
let conceito;
if (media >= 9) {
  conceito = 'A';
} else if (media >= 7) {
  conceito = 'B';
} else if (media >= 5) {
  conceito = 'C';
} else {
  conceito = 'D';
}

// Ternário para escolher entre duas palavras
const situacao = media >= 6 ? 'aprovada' : 'em recuperação';

console.log(`Aluna: ${aluna}`);
console.log(`Média: ${media}`);
console.log(`Conceito: ${conceito}`);
console.log(`Situação: ${situacao}`);

Passo a passo

  1. let conceito; declara a variável antes da cadeia, porque cada bloco vai atribuir um valor diferente a ela; com const isso não seria possível.
  2. O primeiro if testa media >= 9. Para 8.6 a condição é falsa, então o JavaScript passa para o else if seguinte.
  3. media >= 7 é verdadeiro e conceito recebe 'B'. A partir daqui o resto da cadeia é ignorado, mesmo que media >= 5 também seja verdadeiro.
  4. O else final pega qualquer média abaixo de 5, sem precisar de uma condição própria.
  5. media >= 6 ? 'aprovada' : 'em recuperação' escolhe uma das duas palavras numa só expressão e guarda o resultado em uma const.
  6. Os quatro console.log montam o boletim com template literals.

Por que funciona

As faixas estão em ordem decrescente, então a primeira condição verdadeira é exatamente a faixa da média. Cada else if só é avaliado quando todos os anteriores falharam, o que dispensa escrever media >= 7 && media < 9. O ternário é a ferramenta certa para a situação porque há apenas duas saídas e o resultado é um valor, não um bloco de instruções.

Erros comuns

  • Ordem invertida das faixas: começar com if (media >= 5) faz toda média de 5 para cima receber 'C', porque a cadeia para na primeira condição verdadeira. Teste do maior limite para o menor.
  • Vários if independentes: trocar os else if por if soltos faz o 8.6 entrar em >= 7 e depois em >= 5, terminando com 'C'. Use else if para que só um bloco rode.
  • Usar = na condição: if (media = 9) atribui 9 à variável em vez de comparar (e, com const, gera TypeError). Para comparar use >=, === e afins.
  • Declarar conceito dentro do bloco: if (...) { let conceito = 'A'; } cria uma variável que só existe dentro das chaves, e o console.log final gera ReferenceError.

Outra forma de resolver

O conceito também sai de ternários encadeados: const conceito = media >= 9 ? 'A' : media >= 7 ? 'B' : media >= 5 ? 'C' : 'D';. Fica em uma linha e permite usar const, mas com mais de duas ou três faixas a cadeia de if é mais fácil de ler e de alterar.

Saída esperada

Aluna: Bia
Média: 8.6
Conceito: B
Situação: aprovada

02Efeito do dado no tabuleiroMédio

Num jogo de tabuleiro, a peça está na casa 15 e o dado deu 4. Use um switch para definir o efeito do dado (1 e 2: caminho normal; 3: carta de sorte; 4 e 5: vento a favor, com 2 casas extras; 6: jogue de novo; outro valor: jogada inválida) e diga se a peça chegou à casa 20.

↳ Requisitos
  • Usar switch com case agrupados para 1 e 2 e para 4 e 5, cada grupo terminando com break.
  • Tratar valores fora de 1 a 6 no default, com avanço 0.
  • Usar if / else para anunciar a vitória ou quantas casas faltam até a casa 20.
  • A saída deve ser idêntica à saída esperada.
código inicial
const dado = 4;
const posicaoAtual = 15;
const casaFinal = 20;

let avanco = dado;
let efeito;
// defina efeito e avanco com switch

// calcule a nova posição e verifique se chegou ao fim
saída esperada
Dado: 4
Efeito: vento a favor, +2 casas
Posição: 15 -> 21
Chegou ao fim do tabuleiro: vitória!
Ver dica

Dois case seguidos sem nada entre eles levam ao mesmo bloco de código.

Ver resposta
// Efeito do dado em uma partida de tabuleiro
const dado = 4;
const posicaoAtual = 15;
const casaFinal = 20;

let avanco = dado;
let efeito;
switch (dado) {
  case 1:
  case 2:
    efeito = 'caminho normal';
    break;
  case 3:
    efeito = 'tire uma carta de sorte';
    break;
  case 4: // 4 e 5 compartilham o mesmo bloco
  case 5:
    efeito = 'vento a favor, +2 casas';
    avanco = dado + 2;
    break;
  case 6:
    efeito = 'jogue de novo';
    break;
  default:
    efeito = 'valor inválido, a jogada não conta';
    avanco = 0;
}

const novaPosicao = posicaoAtual + avanco;
console.log(`Dado: ${dado}`);
console.log(`Efeito: ${efeito}`);
console.log(`Posição: ${posicaoAtual} -> ${novaPosicao}`);

if (novaPosicao >= casaFinal) {
  console.log('Chegou ao fim do tabuleiro: vitória!');
} else {
  console.log(`Faltam ${casaFinal - novaPosicao} casas.`);
}

Passo a passo

  1. avanco começa igual ao valor do dado, porque na maioria dos casos a peça anda exatamente isso; só o case do vento a favor e o default mudam esse valor.
  2. case 1: seguido de case 2: sem código entre eles agrupa os dois valores: qualquer um deles executa o mesmo bloco.
  3. case 4: e case 5: fazem o mesmo agrupamento e somam 2 casas ao avanço. Com o dado 4, a peça anda 6.
  4. Cada grupo termina com break, que encerra o switch e impede que o bloco seguinte também rode.
  5. O default recebe qualquer valor que não casou com nenhum case (um 7 vindo de um bug, por exemplo) e zera o avanço.
  6. Depois do switch, a nova posição é calculada e o if / else decide entre a mensagem de vitória e a contagem de casas restantes.

Por que funciona

O switch compara dado com cada case usando === e começa a executar a partir do primeiro que casar, seguindo até encontrar um break. É esse comportamento que permite agrupar valores e, ao mesmo tempo, exige o break no fim de cada grupo. Como o valor é um número com poucas opções fixas, o switch deixa a tabela de efeitos mais clara que uma sequência de else if.

Erros comuns

  • Esquecer o break: sem o break depois do vento a favor, a execução cai no case 6 e efeito termina como 'jogue de novo'. Feche cada grupo com break.
  • Comparar com texto: case '4': nunca casa com o número 4, porque o switch usa ===. Mantenha o mesmo tipo dos dois lados.
  • Somar o extra no lugar errado: escrever avanco = 2 em vez de avanco = dado + 2 faz a peça andar só 2 casas. O extra é somado ao valor do dado.
  • Testar só a igualdade com a casa final: if (novaPosicao === casaFinal) falha quando a peça passa da casa 20, como aqui (21). Use >=.

Outra forma de resolver

Uma cadeia de if com || resolve o mesmo problema: if (dado === 4 || dado === 5) { ... } else if (dado === 6) { ... }. Ela é preferível quando as condições envolvem faixas ou várias variáveis; para um único valor comparado com opções fixas, o switch fica mais organizado.

Saída esperada

Dado: 4
Efeito: vento a favor, +2 casas
Posição: 15 -> 21
Chegou ao fim do tabuleiro: vitória!

03Fechamento de pedido com cupomDesafio

Feche um pedido de R$ 180 com o cupom 'PRIMEIRA', de um cliente que já comprou antes e vai pagar no Pix. Aplique as regras do cupom com switch, o frete (grátis pelo cupom ou a partir de R$ 200, senão R$ 15) e 5% de desconto no Pix sobre o valor já com o cupom.

↳ Requisitos
  • Tratar os cupons 'PRIMEIRA' (15% só para cliente novo, senão uma mensagem de recusa), 'DEZOFF' (R$ 10 a partir de R$ 50), 'FRETEGRATIS' e cupom inexistente com switch e default.
  • Calcular o frete com uma única expressão que combine || e o operador ternário.
  • Calcular o desconto do Pix com o operador ternário e exibir todos os valores com duas casas decimais.
  • A saída deve ser idêntica à saída esperada.
código inicial
const subtotal = 180;
const cupom = 'PRIMEIRA';
const clienteNovo = false;
const pagamentoPix = true;

let descontoCupom = 0;
let freteGratis = false;

// aplique o cupom com switch, depois calcule frete, Pix e total
saída esperada
Cupom PRIMEIRA recusado: vale só para a primeira compra.
Subtotal: R$ 180.00
Desconto do cupom: R$ 0.00
Desconto Pix: R$ 9.00
Frete: R$ 15.00
Total: R$ 186.00
Ver dica

Um if pode ficar dentro de um case: o cupom é reconhecido pelo switch, mas só vale se uma segunda condição também for verdadeira.

Ver resposta
// Fechamento de um pedido com cupom, frete e desconto no Pix
const subtotal = 180;
const cupom = 'PRIMEIRA';
const clienteNovo = false;
const pagamentoPix = true;

let descontoCupom = 0;
let freteGratis = false;

// Cada cupom tem uma regra própria
switch (cupom) {
  case 'PRIMEIRA':
    if (clienteNovo) {
      descontoCupom = subtotal * 0.15;
    } else {
      console.log('Cupom PRIMEIRA recusado: vale só para a primeira compra.');
    }
    break;
  case 'DEZOFF':
    descontoCupom = subtotal >= 50 ? 10 : 0;
    break;
  case 'FRETEGRATIS':
    freteGratis = true;
    break;
  default:
    console.log(`Cupom ${cupom} não existe.`);
}

// Frete zero pelo cupom ou por compra acima de R$ 200
const frete = freteGratis || subtotal >= 200 ? 0 : 15;

// O Pix dá 5% sobre o valor já com o desconto do cupom
const descontoPix = pagamentoPix ? (subtotal - descontoCupom) * 0.05 : 0;
const total = subtotal - descontoCupom - descontoPix + frete;

console.log(`Subtotal: R$ ${subtotal.toFixed(2)}`);
console.log(`Desconto do cupom: R$ ${descontoCupom.toFixed(2)}`);
console.log(`Desconto Pix: R$ ${descontoPix.toFixed(2)}`);
console.log(`Frete: R$ ${frete.toFixed(2)}`);
console.log(`Total: R$ ${total.toFixed(2)}`);

Passo a passo

  1. descontoCupom começa em 0 e freteGratis em false: são os valores para quando nenhum cupom se aplica, e cada case só altera o que lhe diz respeito.
  2. No case 'PRIMEIRA', um if / else interno confere se o cliente é novo. Como clienteNovo é false, o desconto continua zero e a mensagem de recusa é impressa logo nesse momento, antes do resumo.
  3. 'DEZOFF' usa um ternário para dar R$ 10 só quando o subtotal atinge R$ 50, e 'FRETEGRATIS' apenas liga a variável freteGratis.
  4. O default avisa sobre qualquer código que não existe, sem mexer nos valores iniciais.
  5. freteGratis || subtotal >= 200 ? 0 : 15 primeiro resolve o || (que tem precedência maior que o ternário) e depois escolhe o frete: aqui as duas partes são falsas, então o frete é 15.
  6. O desconto do Pix é calculado sobre subtotal - descontoCupom, como pede o enunciado: 5% de 180 dá 9.
  7. O total soma tudo e toFixed(2) formata cada valor com duas casas decimais.

Por que funciona

O switch separa as regras por cupom, e cada regra pode ter suas próprias condições, como o if dentro de 'PRIMEIRA'. Os valores iniciais garantem que, quando um cupom é recusado ou não existe, as contas seguintes continuam corretas sem precisar de ramos extras. Frete e Pix têm só duas saídas cada, por isso cabem em ternários, e o || junta numa única condição os dois motivos que zeram o frete.

Erros comuns

  • Aplicar o desconto sem checar o cliente: escrever só descontoCupom = subtotal * 0.15; no case 'PRIMEIRA' dá R$ 27 de desconto a quem já comprou antes. A regra extra precisa de um if dentro do case.
  • Calcular o Pix sobre o subtotal cheio: subtotal * 0.05 ignora o cupom; com um cupom válido, o desconto sairia maior do que deveria. Use (subtotal - descontoCupom) * 0.05.
  • Parênteses no lugar errado: freteGratis || (subtotal >= 200 ? 0 : 15) funciona com o cupom comum, mas com 'FRETEGRATIS' o frete vira true, o total soma 1 em vez de 0 e frete.toFixed(2) gera TypeError. Se quiser deixar a ordem explícita, escreva (freteGratis || subtotal >= 200) ? 0 : 15.
  • Esquecer o break no case 'DEZOFF': a execução segue para 'FRETEGRATIS' e o cliente ganha frete grátis sem ter direito.

Outra forma de resolver

Uma cadeia if (cupom === 'PRIMEIRA' && clienteNovo) { ... } else if (cupom === 'PRIMEIRA') { ... } else if (cupom === 'DEZOFF') { ... } produz o mesmo resultado sem switch. Ela é útil quando as regras dependem de várias variáveis ao mesmo tempo, mas repete o nome do cupom em várias condições; com muitos cupons, o switch deixa cada regra no seu próprio bloco e fica mais fácil de manter.

Saída esperada

Cupom PRIMEIRA recusado: vale só para a primeira compra.
Subtotal: R$ 180.00
Desconto do cupom: R$ 0.00
Desconto Pix: R$ 9.00
Frete: R$ 15.00
Total: R$ 186.00

Últimas lições

Ver o arquivo completo →