1. Fundamentos essenciais da linguagem#
JavaScript é uma linguagem de programação interpretada, dinâmica e multiparadigma, criada originalmente para rodar nos navegadores. Hoje, com ambientes como o Node.js, ela também está presente no servidor, em ferramentas de linha de comando e em aplicações desktop.
Por ter sido projetada para ser acessível e flexível, JavaScript carrega algumas decisões de design que podem surpreender programadores vindos de outras linguagens: tipos que se convertem automaticamente, valores que se comportam diferente dependendo do contexto, e operadores que produzem resultados inesperados se não forem bem compreendidos.
Este capítulo cobre os fundamentos da linguagem que qualquer desenvolvedor JavaScript precisa dominar; não apenas para escrever código que funciona, mas para entender por que ele funciona.
Execução no navegador (console do DevTools)#
Nem sempre é necessário criar arquivos ou abrir uma IDE para testar código JavaScript. Uma das grandes vantagens da linguagem é poder executá-la diretamente no navegador, por meio do console do DevTools.
Para abrir o console:
- Chrome / Edge:
F12ouCtrl + Shift + J - Firefox:
F12ouCtrl + Shift + K
O console permite:
- Executar código JavaScript de forma imediata
- Testar expressões isoladas
- Experimentar funções e estruturas da linguagem
- Inspecionar valores, variáveis e objetos
- Observar erros e comportamentos inesperados
Ao longo desta revisão, o console será nossa principal ferramenta de experimentação.
Exemplo prático:
console.log("Olá, mundo!");
let soma = 10 + 5;
console.log("A soma é:", soma);Observe que:
console.logexibe informações no console;- o JavaScript executa cada linha imediatamente após você pressionar Enter.
let, const e escopo de bloco#
JavaScript possui três formas de declarar variáveis: var, let e const.
Nesta disciplina, não utilizaremos var, pois ela ignora escopo de bloco e pode gerar comportamentos confusos.
Escopo de bloco#
Variáveis declaradas com let e const só existem dentro do bloco {} onde foram declaradas.
for (let i = 0; i < 3; i++) {
console.log(i); // 0, 1, 2
}
// console.log(i); // Erro! 'i' não existe fora do for
Isso evita conflitos e efeitos colaterais, algo essencial quando trabalhamos com sistemas maiores
let#
- Usado quando o valor da variável pode mudar
- Possui escopo de bloco
let idade = 25;
idade = 26; // OK - pode reatribuir
if (true) {
let nome = "Maria";
console.log(nome); // "Maria"
}
// console.log(nome); // Erro! 'nome' só existe dentro do bloco if
const#
- Usado quando a variável não deve ser reatribuída
- Também possui escopo de bloco
const PI = 3.14159;
// PI = 3.14; // Erro! Não pode reatribuir uma constante
const numeros = [1, 2, 3];
numeros[0] = 4; // OK - pode modificar os valores
// numeros = [5, 6]; // Erro! Não pode reatribuir a constante
Regra prática: Use
constsempre que possível. Useletapenas quando precisar reatribuir.
Tipos primitivos: number, string, boolean, undefined, null#
JavaScript possui tipos primitivos, que representam valores simples e imutáveis. Use typeof para inspecionar o tipo de um valor; por exemplo, para descobrir qual o tipo do resultado de uma expressão.
Antes de avançar, vale entender como o JavaScript funciona. Diferente de linguagens mais rígidas, o JavaScript tenta ser flexível, convertendo tipos automaticamente.
Isso gera consequências importantes, como código mais rápido de escrever e menos verboso (menos comandos para executar uma ação). Entretanto, com essas vantagens vêm algumas desvantagens. Um programador iniciante pode criar código repleto de conversões invisíveis, resultados inesperados e bugs difíceis de perceber.
number#
Representa todos os números, inteiros ou decimais.
let inteiro = 42;
let decimal = 3.14;
let negativo = -10;
let infinito = Infinity;
let naoNumero = NaN; // Not a Number
console.log(typeof inteiro); // "number"
Em JavaScript, não existe
intoufloat. Todo tipo numérico énumber.
string#
Representa texto. Pode ser declarada com aspas simples, duplas ou acento grave (crase).
let nome = "João";
let sobrenome = "Silva";
let frase = `Olá, ${nome} ${sobrenome}!`; // Template literal
console.log(typeof nome); // "string"
O uso de template literals (crase) facilita a composição de strings dinâmicas.
boolean#
Representa valores lógicos.
let ativo = true;
let bloqueado = false;
console.log(typeof ativo); // "boolean"
undefined#
Variável declarada mas não inicializada (ou seja, não recebeu valor).
let x;
console.log(x); // undefined
console.log(typeof x); // "undefined"
null#
Representa ausência intencional de valor.
let usuario = null; // Nenhum usuário selecionado
console.log(typeof null); // "object" (comportamento histórico do JS)
Apesar do
typeof,nullnão é um objeto.
O NaN - Not a Number#
NaN significa Not a Number, um valor especial em JavaScript que tem comportamentos únicos.
1. NaN é do tipo number
typeof NaN; // "number"
Isso acontece porque NaN representa um erro dentro do sistema numérico do JavaScript.
2. NaN não é igual a ele mesmo
Este é um dos comportamentos mais estranhos da linguagem:
NaN === NaN; // false
NaN == NaN; // false
Em JavaScript, NaN representa um valor inválido e dois valores inválidos não são considerados iguais.
3. Como detectar corretamente
Nunca compare com NaN diretamente.
Errado: valor === NaN;
Correto: Number.isNaN(valor);
Exemplo:
let x = Number("abc");
Number.isNaN(x); // true
As situações comuns que resultam em NaN são:
Number("abc"); // NaN
0 / 0; // NaN
"texto" - 1; // NaN
Math.sqrt(-1); // NaN
Operadores aritméticos, relacionais e lógicos#
Operadores são os símbolos que dizem ao JavaScript o que fazer com os valores. Toda expressão na linguagem — de um simples cálculo a uma condição complexa — é construída com eles. Nesta seção, você vai conhecer os três grupos principais: os que realizam cálculos, os que comparam valores e os que combinam condições lógicas.
Operadores aritméticos#
Realizam operações matemáticas.
let a = 10,
b = 3;
console.log(a + b); // 13 - Adição
console.log(a - b); // 7 - Subtração
console.log(a * b); // 30 - Multiplicação
console.log(a / b); // 3.333... - Divisão
console.log(a % b); // 1 - Resto da divisão (módulo)
console.log(a ** b); // 1000 - Exponenciação
// Operadores de incremento/decremento
let contador = 5;
contador++; // contador = 6
contador--; // contador = 5
Operadores relacionais#
Comparam valores e retornam boolean.
let x = 10,
y = 5;
console.log(x > y); // true - Maior que
console.log(x < y); // false - Menor que
console.log(x >= 10); // true - Maior ou igual
console.log(x <= 5); // false - Menor ou igual
console.log(x == y); // false - Igual (com conversão de tipo)
console.log(x === y); // false - Estritamente igual
console.log(x != y); // true - Diferente (com conversão)
console.log(x !== y); // true - Estritamente diferente
Operadores lógicos#
Combinam expressões booleanas.
let temCNH = true;
let maiorDeIdade = true;
let estaCansado = false;
// AND (&&) - Retorna true se AMBOS forem true
console.log(temCNH && maiorDeIdade); // true
console.log(temCNH && estaCansado); // false
// OR (||) - Retorna true se PELO MENOS UM for true
console.log(temCNH || estaCansado); // true
console.log(false || false); // false
// NOT (!) - Inverte o valor booleano
console.log(!estaCansado); // true
console.log(!temCNH); // false
Conversão implícita vs explícita#
Uma das características mais marcantes do JavaScript é a sua flexibilidade com tipos. Ao contrário de linguagens mais rígidas, onde misturar um número com uma string gera um erro, o JavaScript tenta tratar a situação por conta própria, convertendo valores automaticamente para que a operação faça sentido. Esse comportamento se chama coerção, e embora pareça conveniente, é uma das principais fontes de bugs em código JavaScript.
Entender quando o JavaScript converte tipos sozinho, e quando você deve fazer isso explicitamente, é essencial para escrever código previsível.
Conversão implícita (coerção)#
JavaScript converte automaticamente tipos em certas operações.
// String + Number = String (concatenação)
console.log("10" + 5); // "105"
console.log("Idade: " + 25); // "Idade: 25"
// Operações matemáticas convertem para number
console.log("10" - 5); // 5
console.log("10" * "2"); // 20
console.log("10" / "2"); // 5
// Boolean em contexto numérico
console.log(true + 1); // 2
console.log(false + 1); // 1
Essas conversões automáticas são uma fonte comum de bugs.
Conversão explícita#
Você força a conversão usando funções.
| |
Na linha 11, você vê uma menção a um operador unário. Este é um operador que atua sobre um único operando (um único valor).
Em JavaScript, alguns operadores unários são usados com frequência para conversão de tipos.
Operador unário + (conversão para número)#
O operador +, quando usado antes de um valor, tenta converter esse valor para o tipo number.
+"42"; // 42
+"3.14"; // 3.14
+"abc"; // NaN
+true; // 1
+false; // 0
Esse operador funciona de forma semelhante a Number():
Number("42"); // 42
Operador unário !! (conversão explícita para boolean)#
O !! não é um operador próprio da linguagem.
Ele é o resultado da aplicação do operador unário de negação (!) duas vezes seguidas.
!valor; // nega o valor lógico
!!valor; // nega a negação, forçando um boolean
Passo a passo do que acontece
Considere o valor#
let x = "texto";Primeira negação (
!x)#- “texto” é um valor truthy (interpretado como
true) !xresulta emfalse
!x; // false- “texto” é um valor truthy (interpretado como
Segunda negação (
!!x)#falseé negado novamente- o resultado final é
true
!!x; // trueOu seja, o JavaScript converte o valor para
booleane retorna essebooleanexplícito.
Exemplos comuns
!!42; // true
!!0; // false
!!"texto"; // true
!!""; // false
!!null; // false
!!undefined; // false
Esses resultados seguem as regras de valores truthy e falsy do JavaScript.
O que é truthy e falsy?#
Em JavaScript, nem todo valor booleano vem de true ou false. Em muitos contextos, a linguagem converte valores automaticamente para booleano. Isso significa que, onde quer que esses valores sejam usados, eles serão interpretados como boolean. Por exemplo, na condição de um if.
Valores falsy
Estes sempre viram false:
false0""(string vazia)nullundefinedNaN
Valores truthy
Todo valor que não está na lista acima é truthy.
Exemplos:
Boolean("0"); // trueBoolean("false"); // trueBoolean([]); // trueBoolean({}); // trueBoolean(42); // true
Comparação com Boolean()
O efeito de !!valor é o mesmo que Boolean(valor)
Exemplo:
Boolean(10); // true
!!10; // true
A diferença está apenas na forma, não no resultado. Quando o !! aparece em código real, é frequentemente usado:
- para garantir que uma expressão resulte em
trueoufalse - em validações rápidas
- em condições complexas
Exemplo:
if (!!usuario) {
console.log("Usuário existe");
}Esse código poderia ser escrito de forma mais legível como:
if (usuario !== null) {
console.log("Usuário existe");
}Apesar de comum em código JavaScript real, o uso de !! não é obrigatório, pode reduzir a legibilidade para iniciantes e não deve ser usado como “atalho” sem entendimento.
O !! foi apresentado aqui apenas como algo que o estudante precisa reconhecer e compreender, não necessariamente usar. Por simplicidade e clareza, prefira Boolean(valor).
Operadores ==, ===, != e !==#
O operador == faz conversão de tipos antes de comparar (coerção), enquanto === compara valor E tipo.
Exemplos comparativos:
// Igualdade com conversão (==)
console.log(5 == "5"); // true - converte "5" para número
console.log(true == 1); // true - converte true para 1
console.log(false == 0); // true - converte false para 0
console.log(null == undefined); // true - caso especial
console.log("" == 0); // true - string vazia vira 0
// Igualdade estrita (===)
console.log(5 === "5"); // false - tipos diferentes
console.log(true === 1); // false - tipos diferentes
console.log(false === 0); // false - tipos diferentes
console.log(null === undefined); // false - tipos diferentes
console.log("" === 0); // false - tipos diferentes
// Casos curiosos
console.log([1] == 1); // true - array é convertido
console.log([1] === 1); // false - tipos diferentes
Recomendação: Use sempre === e !== em vez de == e != para evitar bugs causados por conversões inesperadas.
Atividades sugeridas#
As atividades a seguir devem ser feitas primeiro no papel ou mentalmente, e só depois testadas no console.
- Analise as expressões abaixo e preveja o resultado antes de executar no console:
// Exercício 1
let resultado1 = "5" + 3;
// Exercício 2
let resultado2 = "5" - 3;
// Exercício 3
let resultado3 = "10" * "2";
// Exercício 4
let resultado4 = true + true + false;
// Exercício 5
let resultado5 = "20" / "4" + 5;
// Exercício 6
let resultado6 = 10 > 5 && 3 < 2;
// Exercício 7
let resultado7 = "abc" - 1;
// Exercício 8
let resultado8 = null + 10;
// Exercício 9
let resultado9 = undefined + 5;
// Exercício 10
let resultado10 = "5" === 5;Ver respostas do Exercício 1
"53"(concatenação)2(subtração numérica)20(multiplicação numérica)2(true=1, false=0)10(5 + 5)false(10>5 é true, mas 3<2 é false)NaN(não é possível subtrair de string)10(null vira 0)NaN(undefined não converte bem)false(tipos diferentes)
- Identifique e corrija os erros no código abaixo:
// Código com erro
if (true) {
var mensagem = "Olá";
}
console.log(mensagem); // Funciona com var, mas não é boa prática
Ver correção do Exercício 2
// Código corrigido
let mensagem;
if (true) {
mensagem = "Olá";
}
console.log(mensagem);- Identifique e corrija os erros no código abaixo:
// Código com erro
for (let i = 0; i < 3; i++) {
// ...
}
console.log(i); // Erro! 'i' não existe fora do for
Ver correção do Exercício 3
// Código corrigido
let i;
for (i = 0; i < 3; i++) {
// ...
}
console.log(i); // Agora funciona
- Identifique e corrija os erros no código abaixo:
// Código com erro
const nome = "João";
nome = "Maria"; // Erro! Não pode reatribuir const
Ver correção do Exercício 4
// Código corrigido - use let se precisar reatribuir
let nome = "João";
nome = "Maria"; // OK
- Preveja o resultado (
trueoufalse):
1. "0" == 0 // ?
2. "0" === 0 // ?
3. 0 == false // ?
4. 0 === false // ?
5. null == 0 // ?
6. undefined == null // ?
7. [] == false // ?
8. [] === false // ?
Ver respostas do Exercício 5
true(conversão)false(tipos diferentes)true(conversão)false(tipos diferentes)false(null não converte para número em ==)true(caso especial)true(conversão complexa)false(tipos diferentes)
- Preveja o resultado:
let x = "5";
let y = x * 2;
let z = y + "1";
console.log(z);- Explique o passo a passo de como a expressão a seguir é avaliada:
let resultado = "5" + 5 * 2;
console.log(resultado);- Considere o código a seguir. O que será exibido e por que a saída do código é essa?
let a = Number("abc");
console.log(a === NaN);
console.log(Number.isNaN(a));- Preveja quais linhas imprimem algo:
if ("") console.log("A");
if ("0") console.log("B");
if (0) console.log("C");
if ([]) console.log("D");
if (null) console.log("E");
if ("false") console.log("F");Explique com suas palavras por que
"5" + 5e"5" - 5produzem resultados de tipos diferentes?O código abaixo pode ser interpretado de duas formas diferentes:
let total = "100" + 20;a) Qual é o resultado atual?
b) Por que o JavaScript escolhe esse comportamento?
c) Reescreva o código para forçar concatenação
d) Reescreva o código para forçar soma numérica
- Complete o código:
let valor = Number("texto");
// Imprima "Número inválido" se for NaN