Dia 5 — Condicionais, truthy e falsy
if, else if, else e switch
Decidir o caminho: if, switch e o operador ternário
if escolhe o bloco que roda: uma condição vira true ou false e o programa segue por um caminho só. A condição vai entre parênteses, o corpo entre chaves. Sem as chaves, só a primeira linha depois do if é condicional — o resto do bloco roda sempre, e o erro aparece longe do if.
const idade = 20; if (idade >= 18) { console.log('maior de idade'); } else { console.log('menor de idade'); }
Condições combinadas
&& exige os dois lados verdadeiros, || aceita um só, ! nega. São operadores de boolean e de valor, não comparadores: if (idade) também funciona, porque idade será convertida para true ou false (dia 5, aula 2).
if (idade >= 18 && idade <= 65) { } // os dois lados if (turno === 'noite' || turno === 'madrugada') { } // um basta if (!bloqueado) { } // nega
&& e || devolvem um dos operandos, não true/false: nome || 'sem nome' entrega o nome quando ele existe e o texto padrão quando não. Quando um lado e condição e o outro valor, && lê melhor: usuario && usuario.nome.
A expressao condicional numa linha é o operador ?: — o mesmo que if/else inteiro em forma de valor:
const situacao = idade >= 18 ? 'maior' : 'menor';
Regra de escolha: quando os dois lados são valores, ternário; quando os dois lados são blocos de código, if.
else if e o return antecipado
else if é um if com else que só testa a próxima condição; a primeira que casar vence e as outras nem são avaliadas. Todo if com else if devia ter else no fim, senão o caso que não casa nenhum dos testes passa sem fazer nada e ninguém descobre depois.
O if aninhado é a outra forma de decidir: um if dentro do corpo de outro, quando a segunda decisão só importa para quem já entrou no primeiro. É mais rápido de ler que uma pirâmide de else if, e vira ilegível assim que passar de dois níveis.
function situacao(nota) { if (nota < 0 || nota > 10) { return 'nota invalida'; } else if (nota >= 7) { return 'aprovado'; } else if (nota >= 5) { return 'recuperacao'; } else { return 'reprovado'; } }
O return sai da função imediatamente: é o "corta aqui" do JavaScript, e a forma mais direta de sair da função antes de chegar ao fim. O mesmo raciocínio vale sem else if — quem escreve a condição mais específica primeiro evita o bloco de return repetido.
switch
| Palavra-chave | Onde entra | O que faz |
|---|---|---|
switch | switch (expressao) { } | abre a estrutura e avalia a expressão uma vez |
case | case valor: | compara o valor com a expressão usando === |
break | break; | sai do switch e cai na linha depois do } |
default | default: | roda quando nenhum case casou |
switch é para valor exato: código de estado, tipo de pedido, mês do ano. Compara com === estrito, então switch (tipo) com case '1' não casa com o número 1. A posição do default não muda nada: ele é testado depois de todos os case.
Sem break, o case executa e cai no seguinte (fall-through). É o que permite casos empilhados de propósito:
switch (sigla) { case 'S': case 'SS': console.log('segunda'); break; case 'Q': console.log('quarta'); break; default: console.log('sigla invalida'); }
breakdentro de umswitchque está dentro de um laço sai doswitch, não do laço. Para sair do laço, obreakprecisa estar no corpo do laço — ou oswitchinteiro tem que devolver porreturn. E obreakdocaseé opcional quando cadacasetermina emreturn: sem ele, o próximocaseroda com o mesmo argumento.
Exemplo
'use strict'; // Exemplo: as quatro portas de decisao do JavaScript. // if escolhe por intervalo; switch escolhe por valor exato. const nota = 6.5; // if / else if / else: a primeira condicao verdadeira leva o controle. if (nota >= 7) { console.log('aprovado'); } else if (nota >= 5) { console.log('recuperacao'); } else { console.log('reprovado'); } // && exige os dois lados, || aceita um so, ! nega. const idade = 17; const turno = 'madrugada'; const urgente = false; if (idade >= 18 && turno === 'noite') { console.log('processado na hora'); } if (turno === 'noite' || turno === 'madrugada') { console.log('o || disparou com um lado so:', turno); } if (!urgente) { console.log('o ! negou o false, entao isto imprime'); } // return antecipado: sai da funcao antes do resto do codigo. function situacao(valor) { if (valor < 0 || valor > 10) { return 'nota invalida'; } if (valor >= 7) { return 'aprovado'; } return 'reprovado'; } console.log('situacao(8):', situacao(8), '| situacao(12):', situacao(12)); // switch compara com === estrito: 1 e o numero, nao a letra. function nomeDoDia(numero) { switch (numero) { case 1: return 'segunda'; case 2: return 'terca'; case 6: return 'sabado'; default: return 'dia invalido'; } } console.log('switch:', nomeDoDia(1), nomeDoDia(6), nomeDoDia(9)); // O fall-through de proposito: casos empilhados compartilham o mesmo corpo. function diaUtil(sigla) { switch (sigla) { case 'S': case 'SS': return 'util'; case 'Q': return 'teste'; default: return 'descanso'; } } console.log('diaUtil S, SS, Q, D:', diaUtil('S'), diaUtil('SS'), diaUtil('Q'), diaUtil('D')); // O fall-through sem break: o case 2 continua executando o case 3. let nivel = 0; switch (2) { case 1: nivel = nivel + 10; case 2: nivel = nivel + 100; case 3: nivel = nivel + 1000; break; default: nivel = -1; } console.log('switch(2) sem break no case 2 soma', nivel); // Sombreamento: o const de dentro esconde o de fora e nao altera o de fora. const total = 'global'; if (total === 'global') { const total = 'interno'; console.log('dentro do if:', total); } console.log('fora do if:', total);
Saída real
recuperacao o || disparou com um lado so: madrugada o ! negou o false, entao isto imprime situacao(8): aprovado | situacao(12): nota invalida switch: segunda sabado dia invalido diaUtil S, SS, Q, D: util util teste descanso switch(2) sem break no case 2 soma 1100 dentro do if: interno fora do if: global
Truthy, falsy e o operador ternario
Truthy e falsy: os valores que viram true ou false
Toda condição é convertida em boolean antes de ser testada. O que essa conversão devolve é o que se chama de truthy (vira true) e falsy (vira false). A lista de valores falsos é curta e fechada: são oito, e qualquer outro valor é verdadeiro. Nenhum objeto é falso — nem [], nem {}.
O nome em inglês é truthy e falsy; em português o termo técnico é conversão para booleano. É a mesma regra que decide um if, um while e um &&.
Os valores falsos
São oito, e document.all — o único objeto falso, que só existe no navegador — é o nono caso, à parte. Em cima de tudo isso, o typeof de cada um:
| Valor | typeof | Observação |
|---|---|---|
false | boolean | o literal |
0 | number | o número zero |
-0 | number | o mesmo zero, com sinal contrário |
0n | bigint | zero de BigInt |
"" | string | string vazia; " " com um espaço já é verdade |
null | object | ausência declarada de valor |
undefined | undefined | variável sem valor, ou que não existe |
NaN | number | resultado numérico inválido |
São verdadeiros, contra a intuição: [], {}, "0", "false", Infinity, -1 e qualquer função. new Boolean(false) é verdade, porque o objeto existe — o que está dentro dele não conta. Boolean e !! só olham o valor, nunca o conteúdo.
Converter sem perguntar no console
| Função | Assinatura | Exemplo |
|---|---|---|
Boolean | Boolean(valor): boolean | Boolean("0") devolve true |
!! | !!valor | atalho de Boolean; use dentro de condição |
String | String(valor): string | String(0n) devolve "0", sem o n |
Number | Number(texto): number | Number("") devolve 0 |
parseInt | parseInt(texto, base): number | parseInt("10px") devolve 10 |
typeof | typeof valor: string | typeof naoDeclarada devolve "undefined" sem erro |
!! aplica Boolean duas vezes: a primeira converte, a segunda nega. É o jeito de escrever uma condição booleana sem if, e o que a API do navegador espera quando pede "só quando for verdadeiro".
O ternário
condicao ? valorSeVerdadeira : valorSeFalsa é uma expressão: devolve um valor e cabe onde um valor cabe — em const, dentro de ${}, no argumento de uma função.
const situacao = media >= 7 ? 'aprovado' : 'reprovado'; const total = 1; const lista = `${total} item${total === 1 ? '' : 'ns'}`;
Com o ternário, a regra é: quando os dois lados são valores, ternário; quando os dois lados são blocos de código, if. if (x) { const a = 1; } else { const a = 2; } não é ternário — é if escrito de um jeito que ninguém consegue ler.
switch, ternário ou if: qual usar
O switch é a terceira forma de decidir, e o critério é o tipo de dado. Ele compara a expressão com === contra cada case valor e, sem break no fim, o caso executado cai no seguinte — o fall-through, que é erro na maioria das vezes e proposital em casos empilhados.
| Palavra-chave | Onde entra | O que faz |
|---|---|---|
switch | switch (expressao) { } | avalia a expressão uma vez e abre a estrutura |
case | case valor: | compara com === e, se casar, executa daqui para baixo |
break | break; | sai do switch e pula para a linha depois do } |
default | default: | roda quando nenhum case casou |
A interação com truthy é o que surpreende: como a comparação é ===, o switch não converte tipo. switch (0) não casa com case '0', e NaN não casa com nenhum case, porque nenhuma igualdade com NaN é verdadeira. Um switch sem break e sem return no fim de cada caso é a origem clássica de resultado errado — e é o switch sem break que o professor aponta primeiro quando o resultado sai errado sem erro nenhum.
Quando a decisão cabe numa linha e os dois lados são valores, a expressão condicional (?:) é a condicional compacta: mesmo resultado do if/else, uma linha só, e devolve valor. Quando o que muda é o tipo, vai de switch; quando os dois lados são blocos de código, vai de if.
switch (x = 5)tem um sinal de igual só: o JavaScript lê como atribuição, oxpassa a valer5, e o5é truthy — então odefaultnunca roda e nenhumcasecasa. O teste de igualdade é===. Etypeof nulldevolve"object": é defeito antigo da linguagem,nullnunca foi objeto, e otypeofé a única coisa que ainda mente sobre isso.
Exemplo
'use strict'; // Exemplo: quantos valores sao falsos na pratica, e o que o ternario le. // Tudo que esta no array vira condicao de if em algum lugar do codigo. const valores = [false, 0, -0, 0n, '', ' ', null, undefined, NaN, 1, -1, '0', 'false', [], {}, Infinity]; // Rotulo legivel: String(0n) e "0", igual ao numero 0, entao o tipo entra. function rotulo(valor) { if (typeof valor === 'bigint') { return valor + 'n (bigint)'; } if (typeof valor === 'string') { return JSON.stringify(valor) + ' (string)'; } if (Array.isArray(valor)) { return '[] (array)'; } if (valor === null) { return 'null (object!)'; } if (typeof valor === 'number' && Number.isNaN(valor)) { return 'NaN (number)'; } if (typeof valor === 'object') { return '{} (object)'; } return String(valor) + ' (' + typeof valor + ')'; } console.log('valor'.padEnd(24), 'typeof'.padEnd(9), 'Boolean', '!!'); for (const valor of valores) { console.log(rotulo(valor).padEnd(24), String(typeof valor).padEnd(9), String(Boolean(valor)).padEnd(7), String(!!valor)); } // !! e o atalho de Boolean: converte e nega, sem escrever o if. const nome = ''; console.log('!!nome:', !!nome, '| nome || padrao:', nome || 'sem nome'); console.log('!!lista vazia:', !![], '| !!objeto vazio:', !!{}); // O ternario devolve valor: cabe em const, em interpolacao, em argumento. const total = 1; console.log(`${total} item${total === 1 ? '' : 'ns'}`); const situacao = (itens) => (itens > 0 ? 'com dados' : 'vazia'); console.log('situacao(0):', situacao(0), '| situacao(7):', situacao(7)); // typeof em variavel que nao existe devolve "undefined" e nao levanta erro. console.log('typeof naoDeclarada:', typeof naoDeclarada); // O erro classico: um sinal de igual so. O JavaScript le como atribuicao, // o x passa a valer 5 e a condicao e sempre verdadeira. let x = 0; if (x = 5) { console.log('entrou sempre, e x agora vale', x); }
Saída real
valor typeof Boolean !!
false (boolean) boolean false false
0 (number) number false false
0 (number) number false false
0n (bigint) bigint false false
"" (string) string false false
" " (string) string true true
null (object!) object false false
undefined (undefined) undefined false false
NaN (number) number false false
1 (number) number true true
-1 (number) number true true
"0" (string) string true true
"false" (string) string true true
[] (array) object true true
{} (object) object true true
Infinity (number) number true true
!!nome: false | nome || padrao: sem nome
!!lista vazia: true | !!objeto vazio: true
1 item
situacao(0): vazia | situacao(7): com dados
typeof naoDeclarada: undefined
entrou sempre, e x agora vale 5