Skip to content

JavaScript Folha de referência

A linguagem da web — frontend, backend e além.

01

Primeiros Passos

Hello World & Comentários

JavaScript roda em navegadores e Node.js. console.log() é a principal saída de depuração. Comentários JSDoc (/** */) fornecem informações de tipo e documentação para IDEs. Use 'use strict' ou ES modules para parsing mais seguro. Comentários são ignorados em tempo de execução.

javascript
// Single-line comment
/* Multi-line
   comment */

// Console output
console.log("Hello, World!");
console.warn("Warning message");
console.error("Error message");
console.table([{a: 1}, {a: 2}]);

// JSDoc comments (for documentation)
/**
 * Adds two numbers
 * @param {number} a - First number
 * @param {number} b - Second number
 * @returns {number} The sum
 */
function add(a, b) { return a + b; }

Strict Mode & Módulos

'use strict' habilita parsing mais rigoroso, capturando erros silenciosos. ES Modules (import/export) são o padrão moderno; CommonJS (require/module.exports) é tradicional do Node.js. Sempre use módulos para evitar poluir o escopo global. Navegadores suportam <script type='module'>.

javascript
"use strict";  // enables strict mode (catches common mistakes)
x = 10;  // ReferenceError: x is not defined (strict mode)

// ES Modules (modern)
// import { greet } from './utils.js';
// export function greet(name) { return `Hello, ${name}`; }

// CommonJS (Node.js traditional)
// const fs = require('fs');
// module.exports = { greet: (name) => `Hello, ${name}` };

// Running JS
// Browser: include in <script> or via dev server
// Node.js: node script.js
// Deno: deno run script.ts

Variáveis: let, const, var

Sempre prefira const; use let apenas quando reatribuição for necessária; evite var inteiramente. const previne reatribuição, mas não mutação — conteúdos de objeto/array ainda podem mudar. let/const são block-scoped; var é function-scoped e hoisted (causando bugs). TDZ previne uso de variáveis antes da declaração.

javascript
// const - cannot be reassigned (use by default)
const PI = 3.14159;
const user = { name: "Alice" };
user.name = "Bob";  // OK - object contents can change
// user = {};  // TypeError - can't reassign const

// let - block-scoped, can be reassigned
let count = 0;
count = 1;
if (true) {
  let local = 5;  // only accessible in this block
}

// var - function-scoped, hoisted (AVOID in modern code)
var old = "legacy";  // hoisted, can cause bugs

// Temporal Dead Zone (TDZ)
// console.log(x);  // ReferenceError
// let x = 5;

Tipos de Dados & typeof

JavaScript tem 7 tipos primitivos (string, number, bigint, boolean, undefined, null, symbol) e tipos de referência (objetos, arrays, funções). Primitivos são imutáveis e copiados por valor; objetos são mutáveis e passados por referência. typeof null retorna 'object' devido a um bug histórico — use === null para verificar null.

javascript
// Primitive types (immutable, passed by value)
const str = "hello";        // string
const num = 42;             // number (no separate int/float)
const big = 9007199254740993n;  // bigint
const bool = true;          // boolean
const undef = undefined;    // undefined (no value)
const empty = null;         // null (intentional empty)
const sym = Symbol("id");   // symbol (unique identifier)

// Reference types (mutable, passed by reference)
const arr = [1, 2, 3];      // object (array)
const obj = { a: 1 };       // object
const fn = function() {};   // object (function)

// typeof operator
console.log(typeof "hi");      // "string"
console.log(typeof 42);        // "number"
console.log(typeof true);      // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null);      // "object" (historical bug!)
console.log(typeof []);        // "object"
console.log(typeof {});        // "object"
console.log(typeof function(){}); // "function"

Conversão de Tipos & Coerção

A coerção de tipos do JavaScript é notoriamente confusa. Sempre use === (igualdade estrita) em vez de == (flexível) para evitar coerção inesperada. O operador + concatena se qualquer operando for string; outros operadores coercem para número. Valores falsy: false, 0, '', null, undefined, NaN. Use Number.isNaN() para verificar NaN (NaN !== NaN).

javascript
// Explicit conversion
String(42);        // "42"
(42).toString();   // "42"
Number("42");      // 42
Number("3.14");    // 3.14
Number("");        // 0
Number("abc");     // NaN
parseInt("42px");  // 42
parseFloat("3.14abc");  // 3.14
Boolean(0);        // false
Boolean("");       // false
Boolean("x");      // true

// Implicit coercion (often confusing)
console.log("5" + 3);   // "53" (string concatenation)
console.log("5" - 3);   // 2 (numeric subtraction)
console.log("5" * "2"); // 10
console.log(1 + "2" + 3); // "123"
console.log(true + 1);  // 2

// Falsy values: false, 0, "", null, undefined, NaN
// Everything else is truthy
if ("0") console.log("truthy");  // runs! non-empty string

// Strict vs loose equality
console.log(1 == "1");   // true (loose, coerces)
console.log(1 === "1");  // false (strict, no coercion)
console.log(null == undefined);  // true
console.log(null === undefined); // false
02

Strings

Métodos de String

Strings são imutáveis — métodos retornam novas strings. slice() suporta índices negativos (do final); substring() não. replace() substitui apenas a primeira correspondência; use replaceAll() (ES2021) para todas. at() (ES2022) permite índices negativos. split() + join() é a forma idiomática de substituir caracteres em uma string.

javascript
const s = "Hello, World";

// Length & access
console.log(s.length);     // 12
console.log(s[0]);         // "H"
console.log(s.charAt(0));  // "H"
console.log(s.at(-1));     // "d" (ES2022, negative index)

// Case
console.log(s.toUpperCase());  // "HELLO, WORLD"
console.log(s.toLowerCase());  // "hello, world"

// Search
console.log(s.indexOf("World"));   // 7 (-1 if not found)
console.log(s.includes("World"));  // true
console.log(s.startsWith("Hello")); // true
console.log(s.endsWith("World"));   // true

// Extract
console.log(s.slice(0, 5));    // "Hello"
console.log(s.slice(-5));      // "World"
console.log(s.substring(0, 5)); // "Hello" (no negative)
console.log(s.split(", "));    // ["Hello", "World"]

// Modify
console.log(s.replace("o", "0"));   // "Hell0, World"
console.log(s.replaceAll("o", "0")); // "Hell0, W0rld"
console.log(s.trim());   // remove whitespace
console.log(s.padStart(15, "*")); // "***Hello, World"
console.log("a,b,c".split(",").join("-")); // "a-b-c"

Template Literals

Template literals (backticks) permitem interpolação de strings com ${}, strings multi-linha e tagged templates. Elas são muito mais legíveis que concatenação de strings. Tagged templates permitem processar template literals com uma função — usados por styled-components, graphql-tag, etc.

javascript
const name = "Alice";
const age = 30;

// Template literals (backticks)
const greeting = `Hello, ${name}! You are ${age} years old.`;
console.log(greeting);

// Multi-line strings
const html = `
  <div>
    <h1>${name}</h1>
    <p>Age: ${age}</p>
  </div>
`;

// Expressions inside ${}
console.log(`Next year: ${age + 1}`);
console.log(`Upper: ${name.toUpperCase()}`);
console.log(`${name.length} chars`);

// Tagged templates
function highlight(strings, ...values) {
  return strings.reduce((acc, str, i) =>
    acc + str + (values[i] ? `<b>${values[i]}</b>` : ''), '');
}
const result = highlight`Name: ${name}, Age: ${age}`;
// "Name: <b>Alice</b>, Age: <b>30</b>"

Busca em Strings & Regex

Strings JavaScript suportam regex via match(), matchAll(), replace(), search() e split(). Named capture groups (?<name>...) (ES2018) tornam regex mais legível. matchAll() retorna um iterador (mais eficiente que match() para regex global). Use .test() para verificar se um padrão corresponde sem extrair.

javascript
const text = "The quick brown fox jumps over the lazy dog";

// Search methods
console.log(text.search(/brown/));  // 10 (index of match)
console.log(text.match(/\w+/g));   // ["The","quick","brown",...]
console.log(text.matchAll(/\w+/g)); // iterator of matches

// Replace with regex
console.log(text.replace(/o/g, "0"));  // replace all 'o'
console.log(text.replace(/(\w+)/g, "\$1!"));  // capture group

// Capture groups
const date = "2024-01-15";
const [, year, month, day] = date.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(year, month, day);  // 2024 01 15

// Named capture groups (ES2018)
const m = date.match(/(?<year>\d{4})-(?<month>\d{2})/);
console.log(m.groups.year);   // 2024
console.log(m.groups.month);  // 01

// Test if pattern matches
const emailRe = /^[^@]+@[^@]+\.[^@]+$/;
console.log(emailRe.test("[email protected]"));  // true

Iteração de Strings & Spread

Strings são iteráveis com for...of. O operador spread [...] divide uma string em caracteres — essencial para tratamento correto de emojis (surrogate pairs). Comparação de strings é lexicográfica por unidade de código UTF-16, então use localeCompare() para ordenação locale-aware. Emojis e alguns caracteres têm 2 unidades de código de comprimento.

javascript
const s = "Hello";

// Iterate characters
for (const char of s) {
  console.log(char);  // H, e, l, l, o
}

// Spread into array
const chars = [...s];  // ["H", "e", "l", "l", "o"]
console.log(chars);

// Spread with map/filter
const upper = [...s].map(c => c.toUpperCase()).join("");
console.log(upper);  // HELLO

// String comparison
console.log("a" < "b");   // true (lexicographic)
console.log("apple" < "banana");  // true
console.log("Z" < "a");   // true (uppercase < lowercase in ASCII)

// Locale-aware comparison
console.log("ö".localeCompare("o", "de"));  // locale-specific

// Repeat
console.log("ab".repeat(3));  // "ababab"

// Code points (handles emoji correctly)
const emoji = "😀";
console.log(emoji.length);        // 2 (surrogate pair!)
console.log([...emoji].length);   // 1 (correct)
console.log("😀".codePointAt(0)); // 128512
03

Números & Matemática

Números & Operadores

JavaScript tem um único tipo de número (float de 64 bits) — sem int/float separado. BigInt (sufixo n) lida com inteiros além de 2^53. Aritmética de ponto flutuante tem problemas de precisão (0.1 + 0.2 !== 0.3) — use Number.EPSILON para comparações. ** é o operador de exponenciação (ES2016).

javascript
// JavaScript has one number type (IEEE 754 double)
const int = 42;
const float = 3.14;
const exp = 1e6;      // 1000000
const hex = 0xff;     // 255
const bin = 0b1010;   // 10
const oct = 0o755;    // 493
const big = 9007199254740993n;  // BigInt (arbitrary precision)

// Arithmetic
console.log(10 / 3);    // 3.333...
console.log(10 % 3);    // 1 (remainder)
console.log(2 ** 10);   // 1024 (exponent)
console.log(Math.floor(10 / 3));  // 3
console.log(Math.trunc(-3.7));    // -3 (toward zero)

// Increment/decrement
let x = 5;
console.log(x++);  // 5 (post-increment, returns old)
console.log(++x);  // 7 (pre-increment, returns new)

// Floating point issues
console.log(0.1 + 0.2);          // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);  // false!
// Fix: use Number.EPSILON or round
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);  // true

Objeto Math

O objeto Math fornece constantes e funções. Todas as funções trigonométricas usam radianos. Math.random() retorna [0, 1) — multiplique e faça floor para intervalos inteiros. Para aleatoriedade criptográfica, use crypto.getRandomValues(). Math.max/min não aceitam arrays diretamente — espalhe-os com ....

javascript
// Constants
console.log(Math.PI);       // 3.141592653589793
console.log(Math.E);        // 2.718281828459045
console.log(Math.SQRT2);    // 1.4142135623730951

// Rounding
console.log(Math.round(3.7));   // 4 (nearest)
console.log(Math.floor(3.7));   // 3 (down)
console.log(Math.ceil(3.2));    // 4 (up)
console.log(Math.trunc(-3.7));  // -3 (toward zero)
console.log(Math.sign(-5));     // -1 (sign: -1, 0, or 1)

// Power & roots
console.log(Math.pow(2, 10));   // 1024
console.log(Math.sqrt(144));    // 12
console.log(Math.cbrt(27));     // 3
console.log(Math.abs(-5));      // 5

// Min/Max
console.log(Math.max(1, 5, 3));       // 5
console.log(Math.min(1, 5, 3));       // 1
console.log(Math.max(...[1, 5, 3]));  // 5 (spread array)

// Trigonometry (radians)
console.log(Math.sin(Math.PI / 2));   // 1
console.log(Math.cos(0));             // 1
console.log(Math.PI / 180 * 90);      // radians from degrees

// Random
console.log(Math.random());           // 0 to <1
console.log(Math.floor(Math.random() * 100));  // 0-99 integer

Métodos de Number & Parsing

Sempre especifique a radix (base) para parseInt() — navegadores mais antigos interpretam zeros à esquerda como octal. Number.isNaN() é confiável; isNaN() global faz coerção (isNaN('abc') é true). toFixed() retorna uma string, não um número. Números além de MAX_SAFE_INTEGER perdem precisão — use BigInt.

javascript
// Number methods
const num = 1234.5678;
console.log(num.toFixed(2));      // "1234.57" (string)
console.log(num.toPrecision(3));  // "1.23e+3"
console.log(num.toString(2));     // binary string
console.log((255).toString(16));  // "ff" (hex)

// Number object methods
console.log(Number.isInteger(42));      // true
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isNaN(NaN));         // true (reliable)
console.log(Number.isNaN("NaN"));       // false (global isNaN would be true)
console.log(Number.parseInt("42px"));   // 42
console.log(Number.parseFloat("3.14")); // 3.14

// Parsing strings
console.log(parseInt("42", 10));   // 42 (always specify radix!)
console.log(parseInt("0xff", 16)); // 255
console.log(parseFloat("3.14abc")); // 3.14

// Safe integers
console.log(Number.MAX_SAFE_INTEGER);  // 9007199254740991
console.log(Number.isSafeInteger(2 ** 53));  // false

// NaN checks
const result = Number("abc");  // NaN
console.log(Number.isNaN(result));  // true
console.log(result === NaN);        // false! NaN !== NaN
04

Estruturas de Dados

Arrays

Arrays são dinâmicos, ordenados e podem conter tipos mistos. push/pop são O(1); shift/unshift/splice são O(n). find()/findIndex() recebem uma função predicado. forEach() não retorna nada; use map() para transformar. Arrays são objetos — typeof [] é 'object'. Use Array.isArray() para verificar.

javascript
// Creating arrays
const nums = [1, 2, 3, 4, 5];
const mixed = [1, "hello", true, null];
const empty = new Array(5);  // [empty x 5]

// Access & modify
console.log(nums[0]);      // 1
console.log(nums.length);  // 5
nums[0] = 0;               // modify
console.log(nums.at(-1));  // 5 (negative index, ES2022)

// Add/remove
nums.push(6);          // add to end, returns new length
nums.pop();            // remove from end, returns element
nums.unshift(0);       // add to start
nums.shift();          // remove from start
nums.splice(1, 2);     // remove 2 elements at index 1
nums.splice(1, 0, "a"); // insert at index 1

// Search
console.log(nums.indexOf(3));     // index or -1
console.log(nums.includes(3));    // true/false
console.log(nums.find(n => n > 3));    // first match
console.log(nums.findIndex(n => n > 3)); // index of first match

// Iterate
nums.forEach((val, idx) => console.log(idx, val));
for (const [idx, val] of nums.entries()) {
  console.log(idx, val);
}

Métodos de Array (map, filter, reduce)

map/filter/reduce são a trindade sagrada da programação funcional de arrays. map transforma, filter seleciona, reduce agrega. Eles são encadeáveis e não mutam o original (exceto reverse/sort). sort() converte para strings por padrão — sempre forneça um comparator para números. flat() achata arrays aninhados.

javascript
const nums = [1, 2, 3, 4, 5];

// map - transform each element (returns new array)
const doubled = nums.map(n => n * 2);       // [2, 4, 6, 8, 10]
const withIndex = nums.map((n, i) => `${i}:${n}`);

// filter - keep elements that pass test (returns new array)
const evens = nums.filter(n => n % 2 === 0); // [2, 4]

// reduce - accumulate to single value
const sum = nums.reduce((acc, n) => acc + n, 0);  // 15
const product = nums.reduce((acc, n) => acc * n, 1); // 120
const max = nums.reduce((a, b) => Math.max(a, b));

// Chaining
const result = nums
  .filter(n => n % 2 === 0)   // [2, 4]
  .map(n => n * 10)           // [20, 40]
  .reduce((sum, n) => sum + n, 0); // 60

// Other useful methods
console.log(nums.slice(1, 3));   // [2, 3] (copy portion)
console.log(nums.concat([6, 7])); // [1,2,3,4,5,6,7]
console.log([...nums, 6, 7]);     // spread (modern)
console.log(nums.reverse());      // reverse in-place
console.log(nums.sort((a, b) => a - b)); // numeric sort
console.log([3, 1, 2].sort());   // [1, 2, 3] (string sort!)
console.log(nums.flat());        // flatten one level
console.log([1, [2, [3]]].flat(Infinity)); // [1, 2, 3]

Objetos

Objetos são coleções chave-valor (chaves são strings ou symbols). Notação de ponto para chaves estáticas, notação de colchetes para chaves dinâmicas/especiais. Computed property names {[expr]: val} são ES6. Object.keys/values/entries extraem arrays; Object.fromEntries reverte entries. for...in itera chaves (incluindo herdadas).

javascript
// Object literal
const user = {
  name: "Alice",
  age: 30,
  "is-admin": false,  // keys with special chars need quotes
  greet() {           // method shorthand
    return `Hello, I'm ${this.name}`;
  }
};

// Access
console.log(user.name);       // dot notation
console.log(user["is-admin"]); // bracket notation (for dynamic/special keys)

// Computed property names (ES6)
const key = "dynamic";
const obj = { [key]: "value", [`id_${1}`]: 100 };

// Add/modify/delete
user.email = "[email protected]";  // add
user.age = 31;            // modify
delete user["is-admin"];  // delete

// Check property existence
console.log("name" in user);          // true
console.log(user.hasOwnProperty("name")); // true

// Iterate
for (const key in user) {
  console.log(key, user[key]);
}

// Object methods
console.log(Object.keys(user));    // ["name", "age", ...]
console.log(Object.values(user));  // ["Alice", 31, ...]
console.log(Object.entries(user)); // [["name","Alice"], ...]
console.log(Object.fromEntries([["a", 1]])); // {a: 1}

Destructuring & Spread

Destructuring extrai valores de objetos/arrays de forma concisa. Suporta renomeação (key: newName), defaults (= value) e rest (...rest). Spread (...) expande iterables/objetos — ótimo para mesclar e shallow copy. Object spread sobrescreve chaves duplicadas (último vence). Destructuring em parâmetros de função é poderoso para config opcional.

javascript
// Object destructuring
const user = { name: "Alice", age: 30, email: "[email protected]" };
const { name, age } = user;          // extract by key
const { name: fullName, email = "N/A" } = user; // rename + default
const { ...rest } = user;            // rest pattern
// name -> undefined, fullName -> "Alice", rest -> {age, email}

// Array destructuring
const [a, b, c] = [1, 2, 3];
const [first, , third] = [1, 2, 3];  // skip elements
const [head, ...tail] = [1, 2, 3, 4]; // head=1, tail=[2,3,4]

// Swap variables
let x = 1, y = 2;
[x, y] = [y, x];  // x=2, y=1

// Nested destructuring
const { data: { users } } = response;
const [[a, b], [c, d]] = [[1, 2], [3, 4]];

// Function parameters
function greet({ name, greeting = "Hello" }) {
  return `${greeting}, ${name}`;
}
greet({ name: "Alice" });  // "Hello, Alice"

// Spread operator
const arr1 = [1, 2], arr2 = [3, 4];
const merged = [...arr1, ...arr2];  // [1, 2, 3, 4]
const obj1 = { a: 1 }, obj2 = { b: 2 };
const combined = { ...obj1, ...obj2 };  // {a:1, b:2}
const clone = { ...user };  // shallow copy

Map, Set, WeakMap

Map permite qualquer tipo de chave (objetos, não apenas strings) e mantém ordem de inserção — diferente de objetos plain. Set armazena valores únicos — perfeito para desduplicação. Chaves de WeakMap/WeakSet são fracamente referenciadas (podem ser garbage collected), prevenindo memory leaks. Use Map quando precisar de chaves não-string ou add/delete frequentes.

javascript
// Map - key-value pairs, any key type, maintains insertion order
const map = new Map();
map.set("name", "Alice");
map.set(42, "number key");
map.set({ obj: true }, "object key");

console.log(map.get("name"));    // "Alice"
console.log(map.has("name"));    // true
console.log(map.size);           // 3
map.delete(42);
map.clear();

// Iterate Map
for (const [key, value] of map) {
  console.log(key, value);
}

// Set - unique values
const set = new Set([1, 2, 3, 2, 1]);
console.log(set.size);  // 3 (duplicates removed)
set.add(4);
set.has(2);  // true
set.delete(1);

// Set operations
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
const union = new Set([...a, ...b]);        // {1,2,3,4}
const intersection = new Set([...a].filter(x => b.has(x))); // {2,3}
const difference = new Set([...a].filter(x => !b.has(x)));  // {1}

// WeakMap/WeakSet - keys must be objects, GC-friendly
const weakMap = new WeakMap();
weakMap.set({}, "value");  // key can be garbage collected
05

Fluxo de Controle

If / Else & Ternário

Use if/else para lógica complexa; ternário para seleção simples de valor. && e || fazem short-circuit (útil para defaults/condicionais). ?? (nullish coalescing) verifica apenas null/undefined, diferente de || que verifica todos valores falsy. ?. (optional chaining) acessa propriedades aninhadas com segurança sem erros.

javascript
const score = 85;

// if / else if / else
if (score >= 90) {
  console.log("A");
} else if (score >= 80) {
  console.log("B");
} else {
  console.log("C");
}

// Ternary operator (expression)
const grade = score >= 60 ? "pass" : "fail";

// Nested ternary (avoid - hard to read)
const status = score >= 90 ? "excellent"
             : score >= 80 ? "good"
             : score >= 60 ? "pass"
             : "fail";

// Short-circuit evaluation
const name = user?.name || "Anonymous";  // default value
const value = condition && doSomething(); // execute if true

// Nullish coalescing (??) - only null/undefined, not falsy
const count = 0 ?? 10;  // 0 (not 10, because 0 is not null/undefined)
const name2 = null ?? "default";  // "default"

// Optional chaining (?.)
const city = user?.address?.city;  // undefined if any link is null
const length = user?.name?.length;  // undefined if user or name is null

Switch Statement

switch compara com igualdade estrita (===), então tipo importa. Não esqueça break — sem ele, a execução cai para o próximo case. Agrupe cases empilhando-os (case 6: case 7:). Switch é mais limpo que longas cadeias if/else para valores discretos. Código moderno às vezes prefere tabelas de lookup de objetos.

javascript
const day = 3;

// Traditional switch (use break!)
switch (day) {
  case 1:
    console.log("Monday");
    break;  // without break, falls through!
  case 2:
    console.log("Tuesday");
    break;
  case 6:
  case 7:  // multiple cases share code
    console.log("Weekend");
    break;
  default:
    console.log("Weekday");
}

// Switch with return (no break needed)
function getColor(type) {
  switch (type) {
    case "success": return "green";
    case "error": return "red";
    case "warning": return "yellow";
    default: return "gray";
  }
}

// Strict equality (===)
switch (1) {
  case "1": console.log("string");  // NOT matched
  case 1: console.log("number");    // matched
}

Loops

Use for...of para arrays/strings (valores), for...in para objetos (chaves) — nunca for...in em arrays (itera índices como strings e inclui protótipo). while verifica antes de executar; do...while executa pelo menos uma vez. break sai, continue pula. Use .entries() para obter índice+valor com for...of.

javascript
// for loop
for (let i = 0; i < 5; i++) {
  console.log(i);
}

// for...of (iterable values - arrays, strings, maps)
const fruits = ["apple", "banana", "cherry"];
for (const fruit of fruits) {
  console.log(fruit);
}

// for...in (object keys - AVOID for arrays!)
const user = { name: "Alice", age: 30 };
for (const key in user) {
  console.log(key, user[key]);
}

// while
let count = 0;
while (count < 3) {
  console.log(count);
  count++;
}

// do...while (runs at least once)
let i = 0;
do {
  console.log(i);
  i++;
} while (i < 3);

// break & continue
for (let i = 0; i < 10; i++) {
  if (i === 5) break;      // exit loop
  if (i % 2 === 0) continue; // skip iteration
  console.log(i);
}

// Iterate with index
for (const [index, value] of fruits.entries()) {
  console.log(index, value);
}

Iteradores & Geradores

Iteradores implementam next() retornando {value, done}. Geradores (function*) simplificam criação de iteradores com yield — eles pausam a execução e retomam em next(). Geradores são preguiçosos (computam sob demanda) e podem ser infinitos. Use-os para iterables personalizados, sequências e fluxos async.

javascript
// Iterable protocol (Symbol.iterator)
const range = {
  from: 1, to: 5,
  [Symbol.iterator]() {
    let current = this.from;
    const last = this.to;
    return {
      next() {
        return current <= last
          ? { value: current++, done: false }
          : { done: true };
      }
    };
  }
};

for (const num of range) console.log(num);  // 1, 2, 3, 4, 5
console.log([...range]);  // [1, 2, 3, 4, 5]

// Generator function (function*)
function* idGenerator() {
  let id = 1;
  while (true) {
    yield id++;
  }
}

const gen = idGenerator();
console.log(gen.next());  // {value: 1, done: false}
console.log(gen.next());  // {value: 2, done: false}
console.log(gen.next().value);  // 3

// Generator with yield*
function* nested() {
  yield 1;
  yield* [2, 3, 4];  // delegate to another iterable
  yield 5;
}
06

Funções

Declarações de Função & Expressões

Declarações de função são hoisted (podem ser chamadas antes da definição); expressões não. Arrow functions são concisas e não têm seu próprio 'this' (herdam do escopo envolvente). IIFEs criam escopos privados (menos necessárias com módulos). Funções são first-class — passe-as como argumentos, retorne-as, armazene em variáveis.

javascript
// Function declaration (hoisted - can be called before definition)
greet("Alice");  // works (hoisted)
function greet(name) {
  return `Hello, ${name}!`;
}

// Function expression (not hoisted)
const greet2 = function(name) {
  return `Hi, ${name}!`;
};

// Named function expression (for recursion/stack traces)
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};

// Arrow function (ES6) - concise, no own this
const add = (a, b) => a + b;
const square = x => x * x;
const greet3 = () => "Hello!";
const log = x => { console.log(x); };  // block body needs return

// IIFE (Immediately Invoked Function Expression)
const result = (function() {
  const private = "secret";
  return private.toUpperCase();
})();

// Functions are first-class (can be passed/returned)
function apply(fn, value) {
  return fn(value);
}
console.log(apply(square, 5));  // 25

Arrow Functions & this

Arrow functions não têm seu próprio 'this', 'arguments', 'super' ou 'new.target' — elas herdam do escopo envolvente. Isso as torna perfeitas para callbacks (especialmente em métodos de classe). Mas elas não podem ser usadas como construtores ou métodos que precisam de seu próprio 'this'. Use funções regulares para métodos de objeto.

javascript
// Arrow function variations
const add = (a, b) => a + b;           // implicit return
const greet = name => `Hi ${name}`;    // single param, no parens
const log = () => console.log("hi");   // no params
const obj = (x, y) => ({ x, y });      // return object needs parens
const multi = (a, b) => {              // block body
  const sum = a + b;
  return sum * 2;
};

// Arrow functions don't have their own 'this'
function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++;  // 'this' refers to Timer instance
    console.log(this.seconds);
  }, 1000);
}

// Regular function has its own 'this'
function Counter() {
  this.count = 0;
  // Regular function: 'this' is undefined (strict) or global
  document.addEventListener("click", function() {
    // this.count++;  // ERROR: this is not Counter
  });
  // Arrow function: 'this' is Counter
  document.addEventListener("click", () => {
    this.count++;  // works!
  });
}

// Arrow functions can't be constructors
// const obj = new arrowFunc();  // TypeError

Closures

Closures são funções que 'lembram' as variáveis de seu escopo de definição, mesmo após esse escopo sair. Elas habilitam privacidade de dados (module pattern), memoization, currying e aplicação parcial. Toda função em JavaScript é uma closure. A função interna mantém uma referência a variáveis externas, não uma cópia.

javascript
// A closure is a function that remembers its outer variables
function makeCounter() {
  let count = 0;  // private variable
  return function() {
    return ++count;
  };
}

const counter = makeCounter();
console.log(counter());  // 1
console.log(counter());  // 2
console.log(counter());  // 3
// count is private - can't access directly

// Module pattern (pre-ES6)
const calculator = (function() {
  const result = 0;  // private
  return {
    add(x) { return result + x; },
    multiply(x) { return result * x; }
  };
})();

// Practical: memoization
function memoize(fn) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (!(key in cache)) {
      cache[key] = fn.apply(this, args);
    }
    return cache[key];
  };
}

const slowFib = memoize(n =>
  n < 2 ? n : slowFib(n - 1) + slowFib(n - 2)
);

// Currying with closures
const multiply = a => b => a * b;
const double = multiply(2);
console.log(double(5));  // 10

Arguments & Rest/Spread

Parâmetros padrão fornecem valores de fallback. Rest parameters (...name) coletam argumentos extras em um array real — prefira ao invés do objeto 'arguments' legado. Spread (...) expande um array em argumentos individuais. Destructuring em parâmetros habilita objetos de config nomeados e opcionais — um padrão de API comum.

javascript
// Default parameters
function greet(name = "Guest", greeting = "Hello") {
  return `${greeting}, ${name}!`;
}
greet();              // "Hello, Guest!"
greet("Alice");       // "Hello, Alice!"
greet("Bob", "Hi");   // "Hi, Bob!"

// Rest parameters (...args collects into array)
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4));  // 10

function log(tag, ...args) {
  console.log(tag, args);
}

// Spread (opposite of rest)
const nums = [1, 2, 3];
console.log(Math.max(...nums));  // 3 (spread array as args)
console.log(sum(...nums));       // 6

// arguments object (old way, avoid in modern code)
function old() {
  console.log(arguments);  // array-like, not a real array
  const args = Array.from(arguments);  // convert
}

// Destructuring parameters
function process({ name, age = 0 } = {}) {
  console.log(name, age);
}
process({ name: "Alice" });  // Alice 0
process();  // undefined 0 (default empty object)
07

OOP & Classes

Classes & Construtor

Classes ES6 são açúcar sintático sobre protótipos. Private fields (#name) são ES2022 e verdadeiramente privados (diferente da convenção _name). Getters/setters permitem propriedades computadas. Membros estáticos pertencem à classe, não a instâncias. Class fields (name = value) inicializam propriedades de instância sem construtor.

javascript
class Person {
  // Fields (class fields proposal - ES2022)
  species = "human";  // instance field
  
  // Private fields (ES2022)
  #ssn = "secret";    // truly private
  
  // Static field/method
  static count = 0;
  
  // Constructor
  constructor(name, age) {
    this.name = name;
    this.age = age;
    Person.count++;
  }
  
  // Instance method
  greet() {
    return `Hello, I'm ${this.name}`;
  }
  
  // Getter
  get info() {
    return `${this.name}, ${this.age}`;
  }
  
  // Setter
  set age(value) {
    if (value < 0) throw new Error("Invalid age");
    this._age = value;
  }
  
  get age() {
    return this._age;
  }
  
  // Static method
  static create(name) {
    return new Person(name, 0);
  }
}

const alice = new Person("Alice", 30);
console.log(alice.greet());  // Hello, I'm Alice
console.log(alice.info);     // Alice, 30
console.log(Person.count);   // 1
// alice.#ssn;  // SyntaxError - private!

Herança & Polimorfismo

extends cria herança; super() chama o construtor pai (necessário antes de usar 'this'). Sobrescreva métodos redefinindo-os. JavaScript é herança única, mas mixins (class factories) fornecem composição. instanceof verifica a cadeia de protótipos. Polimorfismo funciona através de sobrescrita de métodos.

javascript
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    return `${this.name} makes a sound`;
  }
  
  // Static method can be inherited
  static create(type, name) {
    return new type(name);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);  // must call super() first
    this.breed = breed;
  }
  
  speak() {
    return `${this.name} barks`;  // override
  }
  
  fetch() {
    return `${this.name} fetches`;
  }
}

class Cat extends Animal {
  speak() {
    return `${this.name} meows`;
  }
}

// Polymorphism
const animals = [new Dog("Rex", "Lab"), new Cat("Whiskers")];
animals.forEach(a => console.log(a.speak()));
// Rex barks
// Whiskers meows

// instanceof checks
const dog = new Dog("Buddy", "Poodle");
console.log(dog instanceof Dog);    // true
console.log(dog instanceof Animal); // true
console.log(dog.constructor.name);  // "Dog"

// Mixins (multiple inheritance alternative)
const Walker = (Base) => class extends Base {
  walk() { return `${this.name} walks`; }
};
class Robot extends Walker(Animal) {}

Protótipos

JavaScript usa herança prototípica — objetos herdam de outros objetos via uma cadeia de protótipos. __proto__ está deprecated; use Object.getPrototypeOf/setPrototypeOf. Classes são açúcar sintático sobre esse sistema. Modificar protótipos integrados (Array.prototype) é perigoso — pode quebrar código. Prefira composição a herança profunda.

javascript
// Every object has a prototype (chain of inheritance)
const obj = {};
console.log(obj.__proto__);  // Object.prototype
console.log(Object.getPrototypeOf(obj));  // preferred

// Constructor function (pre-class syntax)
function OldPerson(name) {
  this.name = name;
}
OldPerson.prototype.greet = function() {
  return `Hi, ${this.name}`;
};
const p = new OldPerson("Alice");
console.log(p.greet());  // Hi, Alice

// Prototype chain
// p -> OldPerson.prototype -> Object.prototype -> null

// Adding to prototype (affects all instances)
Array.prototype.last = function() {
  return this[this.length - 1];
};
console.log([1, 2, 3].last());  // 3

// Object.create (prototypal inheritance)
const animal = { type: "unknown" };
const dog = Object.create(animal);
dog.type = "dog";
console.log(dog.type);  // "dog" (own property)

// Check own vs inherited
console.log(dog.hasOwnProperty("type"));  // true
console.log("type" in dog);  // true (includes inherited)

// Get prototype chain
let proto = Object.getPrototypeOf(dog);
while (proto) {
  console.log(proto);
  proto = Object.getPrototypeOf(proto);
}
08

Tratamento de Erros

Try / Catch / Finally

try/catch/finally lida com exceções. catch vincula o objeto de erro (que tem .message e .stack). Use instanceof para tratar tipos de erro específicos diferentemente. Sempre relance erros desconhecidos após tratar os esperados. Crie erros personalizados herdando Error para tratamento de erros específico da aplicação.

javascript
try {
  const result = JSON.parse('{"invalid json"');
} catch (error) {
  console.error("Parse error:", error.message);
  console.error("Stack:", error.stack);
} finally {
  console.log("Always runs");
}

// Catch specific error types
try {
  const data = JSON.parse(input);
  if (!data.name) throw new TypeError("name is required");
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("JSON syntax error");
  } else if (error instanceof TypeError) {
    console.log("Type error:", error.message);
  } else {
    throw error;  // re-throw unknown errors
  }
}

// Error properties
const err = new Error("Something went wrong");
err.code = "CUSTOM_ERROR";
err.statusCode = 500;
throw err;

// Built-in error types
// Error, TypeError, RangeError, ReferenceError
// SyntaxError, URIError, EvalError

Erros Personalizados

Herdar Error para criar tipos de erro personalizados com contexto extra (campos, códigos). Sempre defina this.name para corresponder ao nome da classe. Use instanceof para capturar tipos de erro específicos. Encadeamento de erros (ES2022 { cause }) preserva o erro original para depuração. Uma boa hierarquia de erros torna o tratamento preciso.

javascript
// Custom error class
class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

class DatabaseError extends Error {
  constructor(message, query) {
    super(message);
    this.name = "DatabaseError";
    this.query = query;
  }
}

// Usage
function validateUser(user) {
  if (!user.email) {
    throw new ValidationError("email", "Email is required");
  }
  if (!user.email.includes("@")) {
    throw new ValidationError("email", "Invalid email format");
  }
}

// Handling custom errors
try {
  validateUser({ name: "Alice" });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(`Validation failed: ${error.field} - ${error.message}`);
  } else if (error instanceof DatabaseError) {
    console.log(`DB error in query: ${error.query}`);
  } else {
    console.log("Unexpected error:", error);
  }
}

// Error chaining (ES2022)
try {
  throw new Error("Original cause");
} catch (cause) {
  throw new Error("Failed to process", { cause });
}
09

Async & Promises

Callbacks

Callbacks são funções passadas para serem chamadas depois. A convenção error-first (err, data) é padrão no Node.js. Callbacks aninhados criam 'callback hell' — código profundamente aninhado e difícil de ler. Promises e async/await resolvem isso. setTimeout/setInterval são APIs comuns baseadas em callback.

javascript
// Callback pattern (old way)
function fetchData(url, callback) {
  setTimeout(() => {
    callback(null, { data: "result" });
  }, 1000);
}

// Callback with error-first convention (Node.js style)
fetchData("/api", (error, data) => {
  if (error) {
    console.error(error);
    return;
  }
  console.log(data);
});

// Callback hell (pyramid of doom)
fetchUser(userId, (err, user) => {
  if (err) return handleError(err);
  fetchPosts(user.id, (err, posts) => {
    if (err) return handleError(err);
    fetchComments(posts[0].id, (err, comments) => {
      if (err) return handleError(err);
      // deeply nested...
    });
  });
});

// Event listeners are callbacks
button.addEventListener("click", (event) => {
  console.log("Clicked!", event.target);
});

// setTimeout / setInterval
setTimeout(() => console.log("After 1s"), 1000);
const id = setInterval(() => console.log("tick"), 1000);
clearInterval(id);  // stop

Promises

Promises representam valores futuros com três estados: pending, fulfilled, rejected. .then() trata sucesso, .catch() trata erros, .finally() sempre executa. Promise.all() espera todas (falha rápido); allSettled() espera todas (nunca falha); race() retorna a primeira settled; any() retorna a primeira bem-sucedida.

javascript
// Creating a promise
const promise = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("Data received");
  } else {
    reject(new Error("Failed"));
  }
});

// Consuming a promise
promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log("Done"));

// Chaining
fetch("/api/users")
  .then(response => response.json())
  .then(users => users.filter(u => u.active))
  .then(active => console.log(active))
  .catch(err => console.error("Error:", err));

// Promise.all - wait for all (fails if any fails)
Promise.all([
  fetch("/api/users").then(r => r.json()),
  fetch("/api/posts").then(r => r.json())
]).then(([users, posts]) => {
  console.log(users, posts);
});

// Promise.allSettled - wait for all (never fails)
Promise.allSettled([p1, p2]).then(results => {
  results.forEach(r => {
    if (r.status === "fulfilled") console.log(r.value);
    else console.log(r.reason);
  });
});

// Promise.race - first to settle wins
Promise.race([p1, p2]).then(first => console.log(first));

// Promise.any - first to succeed
Promise.any([p1, p2]).then(first => console.log(first));

Async / Await

async/await é açúcar sintático sobre promises — faz código async parecer síncrono. 'await' pausa a função até a promise settle. Sempre envolva await em try/catch para tratamento de erros. Use Promise.all() para operações paralelas (mais rápido que await sequencial em um loop). Top-level await funciona em ES modules.

javascript
// async function always returns a Promise
async function fetchData() {
  // await pauses until the promise resolves
  const response = await fetch("/api/users");
  const data = await response.json();
  return data;
}

// Error handling with try/catch
async function getUser(id) {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}`);
    }
    return await res.json();
  } catch (error) {
    console.error("Failed:", error);
    return null;
  }
}

// Sequential vs parallel
async function sequential() {
  const a = await fetch("/api/a");  // waits for a
  const b = await fetch("/api/b");  // then waits for b
  return [a, b];
}

async function parallel() {
  const [a, b] = await Promise.all([  // both at once
    fetch("/api/a"),
    fetch("/api/b")
  ]);
  return [a, b];
}

// Top-level await (ES2022, in modules)
// const data = await fetch("/api").then(r => r.json());

// Iterating with async
async function processUrls(urls) {
  for (const url of urls) {
    const data = await fetch(url);
    console.log(data);
  }
}

// Promise.all with map (parallel)
async function fetchAll(urls) {
  return Promise.all(urls.map(url => fetch(url)));
}
10

Manipulação do DOM

Selecionando & Modificando Elementos

querySelector/querySelectorAll (seletores CSS) são a forma moderna de selecionar elementos. textContent é mais seguro que innerHTML (previne XSS). classList fornece add/remove/toggle/contains para classes. dataset acessa atributos data-*. Sempre sanitize entrada do usuário antes de definir innerHTML.

javascript
// Selecting elements
const el = document.querySelector("#app");        // first match
const all = document.querySelectorAll(".item");    // all matches (NodeList)
const byId = document.getElementById("app");
const byClass = document.getElementsByClassName("item");  // HTMLCollection

// Modifying content
el.textContent = "Hello";        // text only (safe from XSS)
el.innerHTML = "<b>Bold</b>";    // HTML (XSS risk with user input!)
el.innerText = "Visible text";   // respects CSS visibility

// Attributes
el.setAttribute("data-id", "123");
const id = el.getAttribute("data-id");
el.removeAttribute("disabled");
el.dataset.id;  // access data-* attributes
el.id = "new-id";
el.className = "active highlighted";
el.classList.add("active");
el.classList.remove("old");
el.classList.toggle("hidden");
el.classList.contains("active");

// Styles
el.style.color = "red";
el.style.backgroundColor = "blue";  // camelCase
el.style.cssText = "color: red; font-size: 16px;";

// Creating elements
const div = document.createElement("div");
div.textContent = "New element";
div.classList.add("box");
document.body.appendChild(div);
document.body.prepend(div);   // add to beginning
document.body.insertBefore(div, referenceEl);

Eventos

addEventListener é preferido a propriedades on<event> (permite múltiplos listeners). Event delegation (escutar em um parent) é eficiente para elementos adicionados dinamicamente. e.target é o que foi clicado; e.currentTarget é o elemento com o listener. preventDefault() para comportamento padrão; stopPropagation() para bubbling.

javascript
// Add event listener
button.addEventListener("click", (event) => {
  console.log("Clicked!", event.target);
  console.log("Current target:", event.currentTarget);
});

// Common events
// click, dblclick, mousedown, mouseup, mousemove
// keydown, keyup, keypress
// submit, change, input, focus, blur
// load, DOMContentLoaded, resize, scroll

// Event object properties
input.addEventListener("keydown", (e) => {
  console.log(e.key);       // "Enter", "a", etc.
  console.log(e.code);      // "KeyA", "Enter"
  console.log(e.ctrlKey);   // true if Ctrl held
  e.preventDefault();       // stop default behavior
  e.stopPropagation();      // stop bubbling
});

// Event delegation (efficient for many elements)
document.addEventListener("click", (e) => {
  if (e.target.matches(".delete-btn")) {
    const id = e.target.dataset.id;
    deleteItem(id);
  }
});

// Custom events
const customEvent = new CustomEvent("userLogin", {
  detail: { userId: 123 }
});
element.dispatchEvent(customEvent);

element.addEventListener("userLogin", (e) => {
  console.log("User logged in:", e.detail.userId);
});

// Remove listener (must be same function reference)
const handler = () => console.log("click");
button.addEventListener("click", handler);
button.removeEventListener("click", handler);
11

Módulos & JSON

ES Modules

ES Modules (import/export) são o padrão moderno, suportados em navegadores e Node.js. Default export (um por módulo) vs named exports (múltiplos). Dynamic import() habilita lazy loading. Módulos estão sempre em strict mode e têm seu próprio escopo. Use type='module' em tags script HTML.

javascript
// math.js - exporting
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export class Calculator { /* ... */ }

// Default export (one per module)
export default function greet(name) {
  return `Hello, ${name}`;
}

// main.js - importing
import greet from "./math.js";              // default
import { add, PI } from "./math.js";        // named
import * as math from "./math.js";          // namespace
import { add as plus } from "./math.js";    // rename
import greet, { add } from "./math.js";     // default + named

// Dynamic import (returns Promise)
const module = await import("./math.js");
console.log(module.add(2, 3));

// Conditional/dynamic loading
if (featureEnabled) {
  const { default: Feature } = await import("./feature.js");
  new Feature();
}

// Re-export
export { add } from "./math.js";
export * from "./utils.js";

// In HTML
// <script type="module" src="app.js"></script>
// Modules are deferred and in strict mode by default

JSON

JSON.stringify() converte valores JS para strings JSON; JSON.parse() reverte. Use replacers/revivers para filtrar ou transformar durante a conversão. JSON não suporta funções, undefined, Dates (tornam-se strings) ou referências circulares. fetch().json() faz parse de respostas JSON automaticamente. Sempre envolva JSON.parse em try/catch para entrada não confiável.

javascript
// JSON is the standard data interchange format
// JSON supports: string, number, boolean, null, array, object

// JavaScript object to JSON string
const user = { name: "Alice", age: 30, active: true };
const jsonStr = JSON.stringify(user);
// '{"name":"Alice","age":30,"active":true}'

// Pretty print
const pretty = JSON.stringify(user, null, 2);
// {
//   "name": "Alice",
//   "age": 30,
//   "active": true
// }

// JSON string to JavaScript object
const parsed = JSON.parse('{"name":"Bob","age":25}');
console.log(parsed.name);  // "Bob"

// Replacer function (filter/transform during stringify)
const filtered = JSON.stringify(user, (key, value) => {
  if (key === "age") return undefined;  // exclude
  return value;
});

// Reviver function (transform during parse)
const data = JSON.parse(jsonStr, (key, value) => {
  if (key === "date") return new Date(value);
  return value;
});

// Fetch JSON from API
async function getUsers() {
  const res = await fetch("/api/users");
  return res.json();  // parses JSON automatically
}

// JSON limitations
// - No functions, undefined, or dates
// - No circular references (throws error)
// - Keys must be strings (with quotes)

Data & Hora

Date do JavaScript é notoriamente awkward — meses são 0-indexed (Janeiro = 0), dias são 1-indexed. Objetos Date são mutáveis. toISOString() dá UTC; toLocaleString() dá hora local. Para trabalho sério com datas, use uma biblioteca como date-fns ou dayjs. Intl.DateTimeFormat fornece formatação locale-aware.

javascript
// Create dates
const now = new Date();
const specific = new Date("2024-01-15T10:30:00");
const fromMs = new Date(1705315200000);
const fromParts = new Date(2024, 0, 15, 10, 30);  // month is 0-indexed!

// Get components
console.log(now.getFullYear());   // 2024
console.log(now.getMonth());      // 0-11 (January = 0!)
console.log(now.getDate());       // 1-31
console.log(now.getDay());        // 0-6 (Sunday = 0)
console.log(now.getHours());      // 0-23
console.log(now.getTime());       // milliseconds since epoch

// Set components
now.setFullYear(2025);
now.setMonth(11);  // December

// Formatting
console.log(now.toISOString());     // "2024-01-15T10:30:00.000Z"
console.log(now.toLocaleDateString()); // "1/15/2024" (locale)
console.log(now.toLocaleString());  // "1/15/2024, 10:30:00 AM"

// Timestamps
const start = Date.now();  // milliseconds since epoch
// ... do work ...
console.log(`Took ${Date.now() - start}ms`);

// Intl for formatting (modern)
const formatter = new Intl.DateTimeFormat("zh-CN", {
  year: "numeric", month: "long", day: "numeric"
});
console.log(formatter.format(now));  // "2024年1月15日"
12

Recursos do ES6+

let, const & Block Scoping

Prefira const por padrão, let quando reatribuição for necessária, e evite var inteiramente. const previne reatribuição, mas objetos/arrays ainda são mutáveis. let e const são block-scoped e vivem na Temporal Dead Zone antes da declaração (diferente de var que é hoisted como undefined). Isso previne muitos bugs sutis.

javascript
// var is function-scoped (hoisted, leaks out of blocks)
// let and const are block-scoped (stay inside {})
{
  var x = 1;   // accessible outside the block
  let y = 2;   // block-scoped
  const z = 3; // block-scoped, cannot reassign
}
console.log(x); // 1
// console.log(y); // ReferenceError

// const prevents reassignment, NOT mutation
const arr = [1, 2, 3];
arr.push(4);      // OK — mutating the array
// arr = [5];     // TypeError — reassigning const

// Temporal Dead Zone: let/const can't be used before declaration
// console.log(a); // ReferenceError (not undefined like var)
let a = 10;

Arrow Functions & this

Arrow functions são concisas e herdam 'this' do escopo envolvente — perfeitas para callbacks e métodos que precisam do 'this' externo. Mas elas não podem ser usadas como construtores e não têm objeto 'arguments'. Não use arrow functions para métodos de objeto se precisar que 'this' se refira ao objeto (use métodos regulares).

javascript
// Arrow functions: concise syntax, lexically bound 'this'
const add = (a, b) => a + b;
const square = x => x * x;          // single param, no parens
const greet = name => `Hello, ${name}`;  // template literal
const noop = () => {};              // no params, empty body

// Returning an object literal needs parens
const makeUser = (name, age) => ({ name, age });

// Arrow functions DON'T have their own 'this' — they inherit it
function Counter() {
  this.count = 0;
  setInterval(() => {
    this.count++;  // 'this' is the Counter instance (lexical)
    console.log(this.count);
  }, 1000);
}
// Regular function would lose 'this' (it'd be window/undefined)

Destructuring Assignment

Destructuring extrai valores de objetos/arrays em variáveis em uma linha — mais limpo que acesso manual a propriedades. Object destructuring usa { key }, array destructuring usa [index]. Suporta renomeação (key: alias), defaults (key = default), rest (...rest) e padrões aninhados. Fortemente usado em props do React e parâmetros de função.

javascript
// Object destructuring
const user = { name: "Alice", age: 30, city: "NYC" };
const { name, age } = user;
console.log(name, age);  // Alice 30

// Rename and default values
const { name: fullName, country = "USA" } = user;
console.log(fullName, country);  // Alice USA

// Array destructuring
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first, second, rest);  // 1 2 [3,4,5]

// Swap variables (no temp needed!)
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b);  // 2 1

// Destructuring in function parameters
function greet({ name, greeting = "Hello" }) {
  console.log(`${greeting}, ${name}`);
}
greet({ name: "Bob" });  // Hello, Bob

// Nested destructuring
const { data: { results } } = response;

Operadores Spread & Rest

O operador ... é 'spread' ao expandir (em arrays/objetos/chamadas) e 'rest' ao coletar (em params/destructuring). Spread cria shallow copies e mescla objetos (chaves posteriores sobrescrevem anteriores). Rest params substituem o antigo objeto 'arguments' e são Arrays reais. Ambos são idiomas essenciais do JS moderno.

javascript
// Spread (...) expands iterables into individual elements
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];  // [1, 2, 3, 4, 5] (copy + append)
const merged = [...arr1, ...arr2];

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };  // { a: 1, b: 2, c: 3 } (shallow copy)
const updated = { ...obj1, b: 99 };  // override b: { a: 1, b: 99 }

// Rest (...) collects multiple elements into an array
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4);  // 10

// Rest in destructuring
const [head, ...tail] = [1, 2, 3, 4];
console.log(head, tail);  // 1 [2, 3, 4]

// Spread in function calls
Math.max(...[1, 5, 3]);  // 5 (same as Math.max(1, 5, 3))

Template Literals & Tagged Templates

Template literals (backticks) suportam strings multi-linha e interpolação ${} — muito mais limpo que concatenação de strings. Tagged templates permitem que uma função processe as partes literais e valores interpolados, habilitando formatação personalizada, sanitização (ex.: escape de HTML) ou i18n. Popular em styled-components e graphql-tag.

javascript
// Template literals: backticks, multi-line, interpolation
const name = "Alice";
const msg = `Hello ${name},
  this spans
  multiple lines`;

// Expressions inside ${}
const price = 19.99;
const tax = 0.08;
console.log(`Total: ${(price * (1 + tax)).toFixed(2)}`);  // Total: 21.59

// Nested template literals
const items = ["apple", "banana"];
const html = `
  <ul>
    ${items.map(i => `<li>${i}</li>`).join("")}
  </ul>
`;

// Tagged templates: function processes the literal
function highlight(strings, ...values) {
  return strings.reduce((acc, str, i) =>
    acc + str + (values[i] ? `**${values[i]}**` : ""), "");
}
const result = highlight`Name: ${name}, Age: ${30}`;
// "Name: **Alice**, Age: **30**"

Optional Chaining & Nullish Coalescing

Optional chaining (?.) faz short-circuit para undefined se qualquer parte da cadeia for null/undefined — elimina verificações manuais verbosas. Nullish coalescing (??) fornece defaults APENAS para null/undefined, diferente de || que também sobrescreve 0, '' e false. Juntos eles tratam os padrões mais comuns de 'dados ausentes' com segurança. Disponível desde ES2020.

javascript
// Optional chaining (?.): safely access nested properties
const user = { profile: { name: "Alice" } };
console.log(user?.profile?.name);     // "Alice"
console.log(user?.settings?.theme);   // undefined (no error!)
console.log(user?.profile?.address?.city);  // undefined

// Without ?., you'd write:
// user && user.profile && user.profile.name

// Optional method calls
const result = obj?.method?.();
const firstChar = str?.[0];

// Nullish coalescing (??): default only for null/undefined
const theme = user.settings?.theme ?? "light";  // "light"
const count = 0 ?? 10;  // 0 (NOT 10 — ?? keeps falsy but valid values)
const name = "" ?? "Anonymous";  // "" (empty string is kept!)

// vs || which treats all falsy values (0, "", false) as missing
const count2 = 0 || 10;  // 10 (probably not what you want!)
13

Eventos & Tratamento de Eventos

Básico do addEventListener

addEventListener é a forma moderna de vincular eventos — permite múltiplos handlers, suporta event delegation e oferece opções como once, passive e capture. Sempre mantenha uma referência ao handler se precisar removê-lo depois (funções anônimas não podem ser removidas). O objeto de evento carrega target, currentTarget, preventDefault() e stopPropagation().

javascript
// Modern event binding (preferred over onclick=)
const button = document.querySelector("#myBtn");

button.addEventListener("click", function(event) {
  console.log("Clicked!", event.target);
  event.preventDefault();  // stop default action (e.g., form submit)
});

// Multiple listeners can be attached
button.addEventListener("click", handler1);
button.addEventListener("click", handler2);

// Remove a specific listener (must be same reference)
button.removeEventListener("click", handler1);

// Common event types
// "click", "dblclick", "mousedown", "mouseup", "mousemove"
// "keydown", "keyup", "keypress"
// "submit", "change", "input", "focus", "blur"
// "load", "DOMContentLoaded", "resize", "scroll"

// Once option: auto-remove after first trigger
button.addEventListener("click", handler, { once: true });

Event Delegation

Event delegation anexa um listener a um parent que trata eventos de todos os children via event bubbling. Use event.target.matches(selector) para filtrar. Isso é muito mais eficiente que vincular a cada child e lida automaticamente com elementos adicionados dinamicamente. A desvantagem: o parent deve ser um ancestral comum que sempre existe.

javascript
// Delegate events to a parent instead of each child
// Efficient for dynamically added elements
const list = document.querySelector("#item-list");

list.addEventListener("click", (event) => {
  // event.target is the actual element clicked
  if (event.target.matches("li.item")) {
    console.log("Clicked:", event.target.textContent);
    event.target.classList.toggle("selected");
  }
});

// Now dynamically added items automatically work
list.insertAdjacentHTML("beforeend", "<li class='item'>New Item</li>");

// Benefits:
// 1. One listener instead of many (memory efficient)
// 2. Works for elements added after binding
// 3. No need to re-bind when DOM changes

Propagação de Eventos (bubbling & capturing)

Eventos se propagam em três fases: capturing (top-down), target e bubbling (bottom-up, padrão). A maioria dos handlers executa na fase bubbling. stopPropagation() impede o evento de alcançar elementos parent; stopImmediatePropagation() também para outros handlers no mesmo elemento. Use capturing (terceiro arg true) para handlers que devem executar antes dos handlers child.

javascript
<div id="outer">
  <div id="inner">
    <button id="btn">Click</button>
  </div>
</div>

// Events flow in three phases:
// 1. Capturing: top -> target (window -> document -> ... -> target)
// 2. Target: at the target element
// 3. Bubbling: target -> top (default phase most handlers run in)

// Bubbling (default): child fires first, then parents
btn.addEventListener("click", () => console.log("button"));
inner.addEventListener("click", () => console.log("inner"));
outer.addEventListener("click", () => console.log("outer"));
// Click button logs: button -> inner -> outer

// stopPropagation: prevent bubbling to parents
btn.addEventListener("click", (e) => {
  e.stopPropagation();
  console.log("only button");
});

// Capturing phase (third arg = true)
outer.addEventListener("click", handler, true);  // runs during capture

// stopImmediatePropagation: stop other handlers on SAME element too

Eventos Personalizados

CustomEvent permite criar eventos específicos da aplicação com dados de payload na propriedade 'detail'. Combinado com dispatchEvent, isso habilita um padrão pub/sub para desacoplar componentes — módulos se comunicam sem referências diretas. Use uma convenção de nomenclatura como 'namespace:action' para evitar colisões. Essa é a base de muitos frameworks de custom elements.

javascript
// Create and dispatch custom events
const event = new CustomEvent("userLoggedIn", {
  detail: { userId: 42, name: "Alice" },
  bubbles: true,    // allow bubbling
});

document.dispatchEvent(event);

// Listen for the custom event
document.addEventListener("userLoggedIn", (e) => {
  console.log("User logged in:", e.detail.userId, e.detail.name);
});

// Practical: decoupled communication between components
class Cart {
  constructor() {
    this.items = [];
  }
  add(item) {
    this.items.push(item);
    window.dispatchEvent(new CustomEvent("cart:updated", {
      detail: { count: this.items.length }
    }));
  }
}

// Any module can listen without knowing about Cart internals
window.addEventListener("cart:updated", (e) => {
  updateBadge(e.detail.count);
});

Eventos de Teclado & Formulário

Eventos de teclado fornecem e.key (tecla lógica como 'a', 'Enter') e e.code (tecla física como 'KeyA'). Use e.key para a maioria da lógica. Submit de formulário sempre precisa de preventDefault() para parar recarregamento de página. FormData + Object.fromEntries coleta dados de formulário facilmente. 'input' dispara continuamente; 'change' dispara quando o campo perde foco — escolha com base em quando quer validação.

javascript
// Keyboard events
document.addEventListener("keydown", (e) => {
  console.log(e.key, e.code);  // e.key="a", e.code="KeyA"
  if (e.key === "Escape") closeModal();
  if (e.ctrlKey && e.key === "s") { e.preventDefault(); save(); }
});

// Form events
const form = document.querySelector("#myForm");
form.addEventListener("submit", (e) => {
  e.preventDefault();  // stop page reload
  const formData = new FormData(form);
  const data = Object.fromEntries(formData);
  console.log(data);  // { username: "...", email: "..." }
});

// Input validation on change/blur
const emailInput = document.querySelector("#email");
emailInput.addEventListener("blur", () => {
  if (!emailInput.value.includes("@")) {
    emailInput.setCustomValidity("Enter a valid email");
  } else {
    emailInput.setCustomValidity("");
  }
});

// Change vs input:
// 'input' fires on every keystroke; 'change' fires on blur
14

Fetch API & AJAX

fetch Básico (GET)

fetch() é a substituição moderna do XMLHttpRequest — baseada em promises e mais limpa. Crucialmente, fetch apenas rejeita em erros de rede, NÃO em status HTTP de erro (404, 500). Sempre verifique response.ok (status 200-299) antes de fazer parse. Use async/await para código sequencial legível. response.json() é async porque lê o body stream.

javascript
// fetch() returns a Promise; .json() returns another Promise
fetch("https://api.example.com/users")
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    return response.json();  // or .text(), .blob(), .arrayBuffer()
  })
  .then(data => console.log(data))
  .catch(error => console.error("Fetch failed:", error));

// fetch does NOT reject on HTTP errors (404, 500) — only on network
// failures. You MUST check response.ok manually.

// Same with async/await (preferred)
async function getUsers() {
  try {
    const res = await fetch("https://api.example.com/users");
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (err) {
    console.error(err);
  }
}

POST, PUT, DELETE com fetch

O objeto de opções do fetch configura method, headers e body. Para JSON, defina Content-Type: application/json e JSON.stringify o body. Para upload de arquivos, use FormData (não defina Content-Type manualmente — o navegador adiciona o boundary multipart). PUT substitui um recurso inteiramente; PATCH atualiza parcialmente.

javascript
// POST: create a resource
async function createUser(data) {
  const res = await fetch("/api/users", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data),
  });
  return res.json();
}
createUser({ name: "Alice", email: "[email protected]" });

// PUT: update a resource (full replace)
await fetch("/api/users/1", {
  method: "PUT",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Alice Updated" }),
});

// DELETE
await fetch("/api/users/1", { method: "DELETE" });

// Sending form data (file uploads)
const formData = new FormData();
formData.append("file", fileInput.files[0]);
await fetch("/upload", { method: "POST", body: formData });
// Don't set Content-Type for FormData — browser sets it with boundary

Headers de Requisição & Auth

Headers carregam metadados e tokens de auth. Bearer tokens (JWT) vão no header Authorization. Alguns headers são 'proibidos' (controlados pelo navegador) como Host e Cookie. CORS é aplicado pelo navegador, não pelo servidor — você não pode contorná-lo do JS do cliente; o servidor deve enviar Access-Control-Allow-Origin. Requisições OPTIONS preflight acontecem para requisições non-simple.

javascript
// Custom headers (e.g., Bearer token auth)
async function fetchWithAuth(url, options = {}) {
  const token = localStorage.getItem("authToken");
  const res = await fetch(url, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${token}`,
      "Accept": "application/json",
      ...options.headers,  // allow overrides
    },
  });
  return res;
}

// Common headers:
// Content-Type: application/json | application/x-www-form-urlencoded
// Authorization: Bearer <token> | Basic <base64>
// Accept: application/json
// X-Requested-With: XMLHttpRequest (anti-CSRF)

// CORS: browser blocks cross-origin requests unless the server
// returns Access-Control-Allow-Origin. fetch can't bypass CORS.

AbortController (Cancelar Requisições)

AbortController cancela requisições fetch — essencial para search-as-you-type, navegação para longe ou timeouts. Passe signal para fetch; chamar controller.abort() aciona um AbortError. Sem isso, requisições stale podem atualizar a UI fora de ordem. AbortController também funciona com outras APIs async e é o mecanismo de cancelamento padrão no JS moderno.

javascript
// AbortController lets you cancel in-flight fetch requests
const controller = new AbortController();

async function fetchWithTimeout(url, ms = 5000) {
  const timeout = setTimeout(() => controller.abort(), ms);
  try {
    const res = await fetch(url, { signal: controller.signal });
    return await res.json();
  } catch (err) {
    if (err.name === "AbortError") {
      console.log("Request was aborted (timeout or cancel)");
    } else {
      throw err;
    }
  } finally {
    clearTimeout(timeout);
  }
}

// Cancel manually (e.g., user navigated away)
// controller.abort();

// Practical: cancel on new search input
searchInput.addEventListener("input", (e) => {
  controller.abort();  // cancel previous request
  fetchResults(e.target.value);
});

Streaming de Respostas

response.body é um ReadableStream — você pode processar dados em chunks conforme chegam em vez de buffer a resposta inteira na memória. Isso é essencial para arquivos grandes, streaming de logs ou dados em tempo real. Use TextDecoder para streams de texto. O loop reader.read() continua até done ser true. Streaming evita picos de memória em payloads grandes.

javascript
// Read a large response in chunks (streaming)
async function streamJson(url) {
  const res = await fetch(url);
  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });

    // Process complete lines
    const lines = buffer.split("\n");
    buffer = lines.pop();  // keep incomplete line
    for (const line of lines) {
      if (line.trim()) console.log(JSON.parse(line));
    }
  }
}

// Useful for: large files, NDJSON logs, real-time data feeds
// Processes data as it arrives instead of waiting for the full response
15

Web Storage (LocalStorage & SessionStorage)

Básico de localStorage & sessionStorage

localStorage persiste indefinidamente; sessionStorage limpa quando a aba fecha. Ambos armazenam apenas strings — use JSON.stringify/parse para objetos. Storage é síncrono e bloqueia a main thread, então evite armazenar dados grandes. Disponível em todos os navegadores modernos, mas pode estar desabilitado em modo de navegação privada. Capacidade é ~5-10MB por origin.

javascript
// Both store strings only — JSON.stringify objects
// localStorage: persists until cleared (survives browser restart)
// sessionStorage: cleared when the tab closes

// localStorage API
localStorage.setItem("username", "Alice");
const name = localStorage.getItem("username");  // "Alice"
localStorage.removeItem("username");
localStorage.clear();  // remove ALL items

// sessionStorage API (same interface, tab-scoped)
sessionStorage.setItem("tempToken", "abc123");

// Storing objects (must serialize)
const prefs = { theme: "dark", lang: "en" };
localStorage.setItem("prefs", JSON.stringify(prefs));
const loaded = JSON.parse(localStorage.getItem("prefs"));
console.log(loaded.theme);  // "dark"

// Check if storage exists (private mode may disable it)
if (typeof Storage !== "undefined") {
  // localStorage/sessionStorage available
}

Storage Helper com TTL & JSON

Web Storage não tem expiração integrada — este wrapper adiciona TTL (time-to-live) armazenando um timestamp de expiração junto ao valor. Esse é o padrão padrão para cachear respostas de API ou dados de sessão que devem expirar. Sempre envolva acesso a storage em try/catch em produção, pois JSON.parse pode lançar em dados corrompidos e a cota pode ser excedida.

javascript
// A robust storage wrapper with expiration (TTL)
const store = {
  set(key, value, ttlMs) {
    const item = {
      value: value,
      expiry: ttlMs ? Date.now() + ttlMs : null,
    };
    localStorage.setItem(key, JSON.stringify(item));
  },
  get(key) {
    const raw = localStorage.getItem(key);
    if (!raw) return null;
    const item = JSON.parse(raw);
    if (item.expiry && Date.now() > item.expiry) {
      localStorage.removeItem(key);
      return null;  // expired
    }
    return item.value;
  },
  remove(key) { localStorage.removeItem(key); },
};

// Usage: cache data for 5 minutes
store.set("userData", { name: "Alice" }, 5 * 60 * 1000);
console.log(store.get("userData"));  // { name: "Alice" }
// After 5 min: returns null and cleans up

Storage Events (Sincronização Cross-Tab)

O storage event é um canal de comunicação cross-tab integrado — quando uma aba modifica localStorage, todas as outras abas na mesma origin recebem o evento (mas não a aba originadora). Isso habilita sincronizar estado como login/logout, atualizações de carrinho ou mudanças de tema entre abas sem WebSockets. O evento inclui key, oldValue, newValue e url.

javascript
// The 'storage' event fires in OTHER tabs when storage changes
// (not in the tab that made the change)
window.addEventListener("storage", (event) => {
  console.log("Key changed:", event.key);
  console.log("Old value:", event.oldValue);
  console.log("New value:", event.newValue);
  console.log("URL:", event.url);

  if (event.key === "cart") {
    updateCartDisplay(JSON.parse(event.newValue));
  }
});

// Practical: sync logout across tabs
// Tab A: localStorage.setItem("logout", Date.now());
// Tab B: receives storage event -> redirects to login

// Practical: broadcast a message to all tabs
function broadcast(type, data) {
  localStorage.setItem("broadcast", JSON.stringify({ type, data, t: Date.now() }));
}
window.addEventListener("storage", (e) => {
  if (e.key === "broadcast") {
    const msg = JSON.parse(e.newValue);
    handleMessage(msg);
  }
});

IndexedDB (Armazenamento Estruturado Grande)

IndexedDB é um poderoso banco de dados NoSQL no navegador — assíncrono, transacional e capaz de armazenar muito mais dados que localStorage (centenas de MB). Suporta indexes, cursors e transações. A API raw é baseada em callbacks e verbosa; o pacote npm 'idb' fornece um wrapper limpo baseado em Promises. Use IndexedDB para apps offline-first, caches grandes ou dados complexos no client-side.

javascript
// IndexedDB: async, transactional NoSQL store for large data
// Capacity: hundreds of MB to GB (far more than localStorage's 5MB)

// Open a database
const request = indexedDB.open("MyDatabase", 1);

request.onupgradeneeded = (e) => {
  const db = e.target.result;
  // Create an object store (like a table) with a key path
  if (!db.objectStoreNames.contains("users")) {
    db.createObjectStore("users", { keyPath: "id" });
  }
};

request.onsuccess = (e) => {
  const db = e.target.result;
  // Add data in a transaction
  const tx = db.transaction("users", "readwrite");
  const store = tx.objectStore("users");
  store.add({ id: 1, name: "Alice", age: 30 });
  store.add({ id: 2, name: "Bob", age: 25 });

  // Query
  store.get(1).onsuccess = (e) => {
    console.log(e.target.result);  // { id: 1, name: "Alice", age: 30 }
  };
};

// For easier use, consider the 'idb' library (Promise-based wrapper)

Comparação: Cookies vs Storage

Cookies são enviados com toda requisição HTTP (adicionando banda) e são o padrão para auth server-side (session IDs, CSRF tokens). localStorage/sessionStorage são apenas do client e armazenam muito mais dados. IndexedDB é para grandes dados estruturados. Escolha com base em: o servidor precisa disso? (cookie) Quanto de dados? (storage vs IndexedDB) Por quanto tempo? (session vs local). Defina Secure, HttpOnly e SameSite em cookies sensíveis à segurança.

javascript
// COOKIES: sent with every HTTP request, ~4KB limit
document.cookie = "session=abc123; max-age=3600; path=/; Secure; SameSite=Strict";
console.log(document.cookie);  // "session=abc123; theme=dark"

// Set cookie with attributes
document.cookie = "token=xyz; max-age=86400; Secure; HttpOnly; SameSite=Lax";
// Note: HttpOnly can't be set via JS (server-only for security)

// LOCAL STORAGE: ~5-10MB, NOT sent to server, synchronous
localStorage.setItem("theme", "dark");

// SESSION STORAGE: ~5MB, cleared on tab close
sessionStorage.setItem("draft", "work in progress");

// WHEN TO USE WHAT:
// Cookies   -> auth tokens that the server needs to read, server-side sessions
// localStorage -> user preferences, cached data that persists
// sessionStorage -> temporary per-tab state (form drafts, wizard steps)
// IndexedDB -> large datasets, offline data, complex queries
16

Timers (setTimeout, setInterval)

setTimeout & setInterval

setTimeout executa um callback uma vez após um delay; setInterval executa repetidamente. Ambos retornam um ID para cancelamento via clearTimeout/clearInterval. Timers não são precisos — são delays mínimos sujeitos ao event loop, visibilidade da aba (throttled em abas em background) e disponibilidade da main thread. Delays abaixo de 4ms podem ser clamped para 4ms em timers aninhados.

javascript
// setTimeout: run once after a delay (milliseconds)
const timeoutId = setTimeout(() => {
  console.log("Runs after 2 seconds");
}, 2000);

// Cancel before it fires
clearTimeout(timeoutId);

// setInterval: run repeatedly every interval
const intervalId = setInterval(() => {
  console.log("Runs every 1 second");
}, 1000);

// Stop the interval
clearInterval(intervalId);

// Pass arguments to the callback
setTimeout((greeting, name) => {
  console.log(`${greeting}, ${name}`);
}, 1000, "Hello", "Alice");

// NOTE: timers don't guarantee exact timing — they run after the
// minimum delay, but only when the call stack is empty (event loop)

setTimeout Recursivo (Melhor que setInterval)

setTimeout recursivo é preferido a setInterval para tarefas async recorrentes porque garante que a chamada anterior termine antes da próxima começar — sem execuções sobrepostas. Também permite intervalos dinâmicos (ex.: backoff maior em erros). setInterval pode empilhar chamadas se o handler demorar mais que o intervalo, causando problemas de performance.

javascript
// setInterval problems: doesn't wait for the previous call to finish
// If a call takes longer than the interval, they can stack up.

// Better: recursive setTimeout — guarantees the gap between completions
function poll() {
  fetch("/api/status")
    .then(r => r.json())
    .then(data => {
      console.log(data);
      setTimeout(poll, 1000);  // schedule next AFTER this finishes
    })
    .catch(err => {
      console.error(err);
      setTimeout(poll, 5000);  // retry with backoff on error
    });
}
poll();  // start the loop

// This pattern: always waits for the previous call to complete
// before scheduling the next, avoiding overlap/stacking.

requestAnimationFrame (Animações Suaves)

requestAnimationFrame (rAF) é a forma correta de fazer animações visuais — sincroniza com o ciclo de repaint do navegador (~60fps), evita frames desnecessários quando a aba está oculta e produz animações mais suaves que setInterval. Use o parâmetro timestamp para cálculos de delta-time para manter a velocidade de animação consistente em diferentes taxas de atualização. Sempre cancele com cancelAnimationFrame quando terminar.

javascript
// requestAnimationFrame: syncs with the display refresh (~60fps)
// More efficient than setInterval for visual animations
function animate() {
  element.style.left = parseInt(element.style.left || 0) + 2 + "px";
  if (parseInt(element.style.left) < 300) {
    requestAnimationFrame(animate);  // schedule next frame
  }
}
requestAnimationFrame(animate);

// Cancel if needed
const rafId = requestAnimationFrame(animate);
cancelAnimationFrame(rafId);

// Timestamp for delta-time calculations
let lastTime = 0;
function loop(timestamp) {
  const delta = timestamp - lastTime;
  lastTime = timestamp;
  update(delta);  // frame-rate independent movement
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

// rAF pauses in background tabs (saves CPU/battery)

Debounce & Throttle

Debounce espera por uma pausa nas chamadas antes de executar (ex.: pesquisar apenas 300ms após o usuário parar de digitar). Throttle limita a execução a uma vez por intervalo (ex.: atualizar scroll position no máximo a cada 200ms). Ambos previnem problemas de performance de eventos de alta frequência. Debounce = 'agrupar chamadas rápidas em uma'; throttle = 'limitar a taxa de chamadas'.

javascript
// Debounce: delay execution until calls stop for N ms
// Use for: search input, window resize, button spam
function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);  // reset the countdown
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

const search = debounce((query) => {
  fetchResults(query);
}, 300);
input.addEventListener("input", (e) => search(e.target.value));

// Throttle: execute at most once per N ms
// Use for: scroll, mousemove, drag handlers
function throttle(fn, limit) {
  let inThrottle = false;
  return function(...args) {
    if (!inThrottle) {
      fn.apply(this, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

const onScroll = throttle(() => {
  console.log("Scroll position:", window.scrollY);
}, 200);
window.addEventListener("scroll", onScroll);

setTimeout com Promise (delay)

Envolver setTimeout em uma Promise cria uma função 'delay' limpa para async/await — muito mais legível que cadeias de callbacks. Esse padrão habilita lógica de retry com backoff exponencial (espere 1s, 2s, 4s entre retries), animações sequenciais e rate limiting. O helper delay é um dos pequenos utilitários mais úteis no JS async moderno.

javascript
// A promise-based delay for use with async/await
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function countdown() {
  console.log("3...");
  await delay(1000);
  console.log("2...");
  await delay(1000);
  console.log("1...");
  await delay(1000);
  console.log("Go!");
}
countdown();

// Retry with exponential backoff
async function fetchWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const res = await fetch(url);
      if (res.ok) return await res.json();
      throw new Error(`HTTP ${res.status}`);
    } catch (err) {
      if (i === retries - 1) throw err;
      const wait = Math.pow(2, i) * 1000;  // 1s, 2s, 4s
      console.log(`Retry ${i + 1} in ${wait}ms`);
      await delay(wait);
    }
  }
}
17

Map, Set & WeakMap

Map (Chave-Valor com Qualquer Tipo de Chave)

Map é uma coleção chave-valor adequada onde chaves podem ser de qualquer tipo (objetos, funções, números) — diferente de Objects que coercem chaves para strings. Map preserva ordem de inserção, tem uma propriedade .size e é diretamente iterável. Use Map quando precisar de chaves não-string, adições/exclusões frequentes ou quando a coleção não é um record/DTO. Use Object para dados de forma fixa.

javascript
// Map: like an Object but keys can be ANY type (not just strings)
const map = new Map();

// Keys can be objects, functions, primitives
const objKey = { id: 1 };
map.set("name", "Alice");
map.set(objKey, "data for this object");
map.set(42, "numeric key");
map.set(true, "boolean key");

console.log(map.get("name"));   // "Alice"
console.log(map.get(objKey));   // "data for this object"
console.log(map.size);          // 4
console.log(map.has(42));       // true

// Iteration preserves insertion order
for (const [key, value] of map) {
  console.log(key, value);
}

// Map vs Object:
// - Map keys can be any type; Object keys are strings/symbols
// - Map has .size; Object needs Object.keys().length
// - Map is iterable by default; Object needs Object.entries()
// - Map performs better for frequent add/remove

Set (Valores Únicos)

Set armazena valores únicos — perfeito para desduplicação e teste de pertinência. Converter um array para Set e de volta ([...new Set(arr)]) é a forma idiomática de remover duplicatas. Set.has() é O(1) vs Array.includes() que é O(n), então use Set para grandes coleções que você verifica frequentemente. Set não tem map/filter — espalhe para array primeiro.

javascript
// Set: collection of unique values (no duplicates)
const set = new Set([1, 2, 3, 2, 1]);
console.log(set);        // Set(3) { 1, 2, 3 }
console.log(set.size);   // 3

set.add(4);
set.add(4);  // ignored (already exists)
console.log(set.has(3));  // true
set.delete(2);

// Iterate (preserves insertion order)
for (const item of set) {
  console.log(item);
}

// Common use cases:
// 1. Remove duplicates from an array
const unique = [...new Set([1, 2, 2, 3, 3, 3])];  // [1, 2, 3]

// 2. Check for membership (faster than Array.includes for large sets)
const validIds = new Set([101, 102, 103]);
if (validIds.has(userId)) { /* ... */ }

// 3. Set operations (union, intersection)
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
const union = new Set([...a, ...b]);        // {1,2,3,4}
const intersect = new Set([...a].filter(x => b.has(x)));  // {2,3}

WeakMap & WeakSet (Referências Memory-Safe)

WeakMap e WeakSet mantêm referências fracas a objetos — quando nenhuma outra referência existe, a entrada é garbage collected automaticamente. Isso previne memory leaks ao associar dados com elementos DOM ou outros objetos. Chaves devem ser objetos. WeakMap/WeakSet não são iteráveis e não têm .size porque entradas podem desaparecer a qualquer momento durante GC. Use-os para caching/metadata vinculados ao tempo de vida de objetos.

javascript
// WeakMap: keys must be objects; references are "weak"
// (doesn't prevent garbage collection of the key object)
const weakMap = new WeakMap();

let user = { name: "Alice" };
weakMap.set(user, "metadata");

console.log(weakMap.get(user));  // "metadata"

user = null;  // remove the only strong reference
// Now the object can be GC'd, and WeakMap entry disappears automatically

// Practical: attach data to DOM elements without memory leaks
const elementData = new WeakMap();
function cacheData(element, data) {
  elementData.set(element, data);
}
// When the DOM element is removed, its data is auto-cleaned

// WeakSet: collection of objects, weakly held
const processed = new WeakSet();
function processOnce(obj) {
  if (processed.has(obj)) return;  // already done
  processed.add(obj);
  doWork(obj);
}

// WeakMap/WeakSet are NOT iterable (no .size, no for...of)

Iteração & Conversão de Map

Maps são iteráveis em ordem de inserção via for...of (entries por padrão), .keys(), .values() ou .forEach(). Converta entre Maps e Objects usando Object.entries() e Object.fromEntries(). Essa conversão bidirecional é útil quando APIs esperam objetos plain, mas você quer recursos do Map internamente. Lembre-se: chaves de Object se tornam tipo string na conversão.

javascript
const map = new Map([
  ["name", "Alice"],
  ["age", 30],
  ["city", "NYC"],
]);

// Iterate entries (default)
for (const [key, value] of map) {
  console.log(`${key} = ${value}`);
}

// Iterate keys only
for (const key of map.keys()) {
  console.log(key);
}

// Iterate values only
for (const value of map.values()) {
  console.log(value);
}

// forEach (callback style)
map.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});

// Convert to Array
const entries = [...map.entries()];  // [["name","Alice"], ["age",30], ...]
const keys = [...map.keys()];        // ["name", "age", "city"]
const values = [...map.values()];    // ["Alice", 30, "NYC"]

// Convert Object to Map and back
const obj = { a: 1, b: 2 };
const mapFromObj = new Map(Object.entries(obj));
const objFromMap = Object.fromEntries(map);

Escolhendo entre Map, Set, Object & Array

Escolha a coleção certa: Arrays para listas ordenadas com duplicatas e acesso por índice; Objects para records de forma fixa e JSON; Maps para coleções chave-valor dinâmicas com qualquer tipo de chave; Sets para unicidade e lookup rápido. Usar a estrutura errada leva a código verboso e problemas de performance — ex.: verificar Array.includes() em um loop vs Set.has().

javascript
// Decision guide:

// Use ARRAY when:
// - You need ordered data with duplicates
// - You need index-based access (arr[5])
// - You'll map/filter/reduce frequently
const todoList = ["buy milk", "walk dog"];

// Use OBJECT when:
// - You have a fixed known shape (like a user record)
// - You need JSON serialization (JSON.stringify)
// - Keys are strings and known at write time
const user = { name: "Alice", age: 30 };

// Use MAP when:
// - Keys are not strings (objects, functions, numbers)
// - You frequently add/remove key-value pairs
// - You need to iterate in insertion order
// - The collection size changes dynamically
const handlers = new Map();
handlers.set(buttonElement, () => onClick());

// Use SET when:
// - You need unique values (no duplicates)
// - You need fast membership testing (.has is O(1))
// - Order doesn't matter much
const seen = new Set();
if (!seen.has(url)) { seen.add(url); visit(url); }
18

Geradores & Iteradores

Iteradores & Symbol.iterator

Um iterador tem um método next() retornando {value, done}. Um iterable implementa Symbol.iterator, que retorna um iterador. Iterables integrados (arrays, strings, Maps, Sets) funcionam com for...of, spread (...), destructuring e Array.from(). Implementar Symbol.iterator permite que seus objetos personalizados funcionem com todos esses recursos de linguagem de forma transparente.

javascript
// An iterator is an object with a next() method returning {value, done}
function makeIterator(arr) {
  let i = 0;
  return {
    next() {
      return i < arr.length
        ? { value: arr[i++], done: false }
        : { value: undefined, done: true };
    },
  };
}

const it = makeIterator(["a", "b", "c"]);
console.log(it.next());  // { value: "a", done: false }
console.log(it.next());  // { value: "b", done: false }
console.log(it.next());  // { value: "c", done: false }
console.log(it.next());  // { value: undefined, done: true }

// An iterable implements Symbol.iterator (returns an iterator)
const range = {
  from: 1, to: 3,
  [Symbol.iterator]() {
    let current = this.from;
    const last = this.to;
    return {
      next() {
        return current <= last
          ? { value: current++, done: false }
          : { done: true };
      },
    };
  },
};

// Now range works with for...of, spread, etc.
console.log([...range]);  // [1, 2, 3]

Funções Geradoras (function*)

Funções geradoras (function*) usam yield para pausar e retomar a execução — elas produzem valores de forma preguiçosa, um por vez. Isso habilita sequências infinitas, lazy evaluation e pipelines eficientes em memória. Geradores são tanto iteradores quanto iterables. Cada chamada para next() executa até o próximo yield (ou return). Eles são a base de async generators e padrões de coroutine do JS.

javascript
// Generators: pause execution with 'yield', resume with next()
function* idGenerator() {
  let id = 1;
  while (true) {
    yield id++;  // pause here, return value, resume on next()
  }
}

const gen = idGenerator();
console.log(gen.next());  // { value: 1, done: false }
console.log(gen.next());  // { value: 2, done: false }
console.log(gen.next());  // { value: 3, done: false }
// Infinite sequence — only computes values on demand

// Generators are iterables (work with for...of)
function* take(n, iterable) {
  for (const item of iterable) {
    if (n-- <= 0) return;
    yield item;
  }
}

function* naturalNumbers() {
  let n = 1;
  while (true) yield n++;
}

const firstFive = [...take(5, naturalNumbers())];
console.log(firstFive);  // [1, 2, 3, 4, 5]

yield* (Delegar para Outro Gerador)

yield* delega todos os yields para outro iterable ou gerador — achatando estruturas aninhadas e compondo geradores de forma limpa. É o equivalente JS do 'yield from' do Python. Os valores do gerador delegado são produzidos um por um como se fossem parte do gerador externo. Essa é a forma padrão de compor geradores recursivamente.

javascript
// yield* delegates to another iterable/generator
function* inner() {
  yield "a";
  yield "b";
}

function* outer() {
  yield 1;
  yield* inner();     // delegates: yields "a", then "b"
  yield* [10, 20];    // also works with arrays
  yield 2;
}

console.log([...outer()]);  // [1, "a", "b", 10, 20, 2]

// Practical: flatten nested structures
function* flatten(arr) {
  for (const item of arr) {
    if (Array.isArray(item)) {
      yield* flatten(item);  // recursive delegation
    } else {
      yield item;
    }
  }
}

const nested = [1, [2, [3, 4]], 5];
console.log([...flatten(nested)]);  // [1, 2, 3, 4, 5]

send() do Gerador via next(value)

Geradores suportam comunicação bidirecional: next(value) passa um valor (torna-se o resultado da última expressão yield), e throw(error) injeta uma exceção no ponto de yield. Isso habilita coroutines, state machines e os padrões async baseados em geradores que impulsionaram as primeiras implementações de async/await. A primeira chamada next() não pode passar um valor (nada para recebê-lo ainda).

javascript
// next(value) passes a value INTO the generator (becomes yield's result)
function* conversation() {
  const name = yield "What's your name?";  // receives via next()
  const age = yield `Hello ${name}, how old are you?`;
  return `${name} is ${age} years old`;
}

const talk = conversation();
console.log(talk.next().value);          // "What's your name?"
console.log(talk.next("Alice").value);   // "Hello Alice, how old are you?"
console.log(talk.next(30).value);        // "Alice is 30 years old"

// throw(error) injects an exception at the yield point
function* safeGen() {
  try {
    yield "step 1";
  } catch (e) {
    console.log("Caught:", e.message);
    yield "recovered";
  }
}
const g = safeGen();
g.next();           // "step 1"
g.throw(new Error("oops"));  // "Caught: oops" -> "recovered"

Async Generators

Async generators (async function*) combinam geradores com async/await — cada yield pode produzir um valor após um await. Consuma-os com for await...of. Isso é ideal para APIs paginadas, streaming de dados ou qualquer cenário onde você produz valores assincronamente. Eles são a forma moderna de lidar com streaming de dados no JS sem callbacks ou encadeamento manual de promises.

javascript
// async function* : yields promises, can use await
async function* fetchPages(url) {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page}`);
    const data = await res.json();
    if (data.items.length === 0) break;  // no more pages
    yield data.items;
    page++;
  }
}

// Consume with for await...of
(async () => {
  for await (const items of fetchPages("/api/products")) {
    console.log(`Got ${items.length} items`);
    renderItems(items);
  }
  console.log("All pages loaded");
})();

// Practical: stream data as it arrives
async function* streamLines(response) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop();
    for (const line of lines) yield line;
  }
}
19

Proxy & Reflect

Básico de Proxy (Interceptar Operações)

Proxy permite interceptar e personalizar operações fundamentais em um objeto — get, set, has, deleteProperty, ownKeys e mais. O objeto handler define 'traps' (como getters/setters, mas para todas as propriedades). Casos de uso: validação, logging, valores padrão, controle de acesso, sistemas reativos (Vue 3 usa Proxy para reatividade). Retorne true de set para indicar sucesso em strict mode.

javascript
// Proxy wraps an object and intercepts fundamental operations
const target = { name: "Alice", age: 30 };

const handler = {
  get(obj, prop) {
    console.log(`Reading ${prop}`);
    return prop in obj ? obj[prop] : "N/A";  // default value
  },
  set(obj, prop, value) {
    console.log(`Setting ${prop} = ${value}`);
    if (prop === "age" && value < 0) return false;  // validation
    obj[prop] = value;
    return true;  // indicate success
  },
};

const proxy = new Proxy(target, handler);
console.log(proxy.name);   // logs "Reading name", returns "Alice"
proxy.age = 25;            // logs "Setting age = 25"
// proxy.age = -5;         // logs "Setting age = -5", rejected
console.log(proxy.unknown); // logs "Reading unknown", returns "N/A"

Casos de Uso Comuns de Proxy

Proxies habilitam metaprogramação poderosa: índices negativos, computação lazy de propriedades, views read-only, validação, logging e data binding reativo. O sistema de reatividade do Vue 3 usa Proxy para rastrear acesso a propriedades e acionar re-renders automaticamente. A desvantagem é uma pequena sobrecarga de performance, então use Proxies onde a abstração valha a pena, não para todo objeto.

javascript
// 1. Negative array indices (like Python)
function negativeArray(arr) {
  return new Proxy(arr, {
    get(target, prop) {
      const idx = Number(prop);
      if (Number.isInteger(idx)) {
        return target[idx < 0 ? target.length + idx : idx];
      }
      return target[prop];
    },
  });
}
const arr = negativeArray(["a", "b", "c"]);
console.log(arr[-1]);  // "c"

// 2. Auto-populating / lazy properties
const lazy = new Proxy({}, {
  get(target, prop) {
    if (!(prop in target)) {
      target[prop] = expensiveCompute(prop);  // compute on first access
    }
    return target[prop];
  },
});

// 3. Read-only object
const readOnly = new Proxy(data, {
  set() { throw new Error("This object is read-only"); },
  deleteProperty() { throw new Error("Cannot delete"); },
});

API Reflect

Reflect fornece as mesmas operações que Proxy traps interceptam — use-o dentro de traps para encaminhar ao comportamento padrão de forma limpa. Métodos Reflect retornam booleans (sucesso/falha) em vez de lançar, tornando-os mais seguros para lógica condicional. Reflect.ownKeys retorna todas as chaves (strings E symbols), diferente de Object.keys que retorna apenas chaves de string enumeráveis. Juntos, Proxy e Reflect formam o toolkit de metaprogramação do JS.

javascript
// Reflect provides default behavior for proxy traps and
// functional equivalents of object operations

const obj = { x: 1, y: 2 };

// Reflect.get / Reflect.set (instead of obj[prop])
console.log(Reflect.get(obj, "x"));    // 1
Reflect.set(obj, "z", 3);              // obj.z = 3
console.log(Reflect.has(obj, "x"));    // true (like "x" in obj)
Reflect.deleteProperty(obj, "y");      // delete obj.y
console.log(Reflect.ownKeys(obj));     // ["x", "z"]

// In a Proxy, use Reflect to forward to the default behavior
const proxy = new Proxy({}, {
  get(target, prop, receiver) {
    console.log(`get ${prop}`);
    return Reflect.get(target, prop, receiver);  // default behavior
  },
  set(target, prop, value, receiver) {
    console.log(`set ${prop}`);
    return Reflect.set(target, prop, value, receiver);
  },
});

// Reflect.construct: call a constructor with an array of args
const instance = Reflect.construct(Array, [1, 2, 3]);

Objeto Reativo com Proxy (estilo Vue)

Essa é a ideia central por trás da reatividade do Vue 3 — um Proxy intercepta get (para rastrear quais effects dependem de uma propriedade) e set (para acionar esses effects quando a propriedade muda). Isso habilita atualizações de UI declarativas: você muta o estado e o framework re-renderiza automaticamente. Proxies tornaram isso possível no ES6; frameworks anteriores (Vue 2) usavam Object.defineProperty com limitações.

javascript
// A minimal reactive system using Proxy
function reactive(target) {
  const subscribers = new Set();

  const handler = {
    get(obj, prop) {
      track(prop);  // record who reads this property
      const value = Reflect.get(obj, prop);
      return typeof value === "object" && value !== null
        ? reactive(value)  // deeply reactive
        : value;
    },
    set(obj, prop, value) {
      const result = Reflect.set(obj, prop, value);
      trigger(prop);  // notify subscribers
      return result;
    },
  };

  let currentEffect = null;
  function track(prop) {
    if (currentEffect) subscribers.add(currentEffect);
  }
  function trigger(prop) {
    subscribers.forEach(fn => fn());
  }

  return new Proxy(target, handler);
}

const state = reactive({ count: 0 });
// When state.count changes, effects re-run automatically
20

Web Workers

Criando um Web Worker

Web Workers executam JavaScript em uma thread background separada, habilitando paralelismo verdadeiro sem bloquear a UI. A main thread e o worker se comunicam via postMessage (dados são copiados/structured-cloned, não compartilhados). Workers não podem acessar o DOM ou objeto window — eles são isolados. Use workers para tarefas CPU-intensive como processamento de imagem, parse de arquivos grandes ou cálculos complexos.

javascript
// main.js — workers run JS in a background thread
const worker = new Worker("worker.js");

// Send data to the worker
worker.postMessage({ command: "calculate", data: [1, 2, 3, 4, 5] });

// Receive results from the worker
worker.onmessage = (event) => {
  console.log("Result from worker:", event.data);
};

worker.onerror = (error) => {
  console.error("Worker error:", error.message);
};

// Terminate when done (frees resources)
// worker.terminate();

// --- worker.js (separate file) ---
// self.onmessage = (event) => {
//   const { command, data } = event.data;
//   const result = heavyComputation(data);
//   self.postMessage(result);
// };

Inline Workers (Blob URL)

Inline workers criam um Worker a partir de uma string de código via Blob URL — sem arquivo separado necessário. Isso é útil para demos, pequenos utilitários ou quando seu build system não lida facilmente com arquivos worker separados. Lembre-se de revogar a object URL para evitar memory leaks. O código do worker é uma string, então você perde syntax highlighting do editor e verificação de tipos — use com cuidado em produção.

javascript
// Create a worker from a string (no separate file needed)
const workerCode = `
  self.onmessage = function(e) {
    const result = e.data.map(x => x * x);
    self.postMessage(result);
  };
`;

const blob = new Blob([workerCode], { type: "application/javascript" });
const worker = new Worker(URL.createObjectURL(blob));

worker.postMessage([1, 2, 3, 4]);
worker.onmessage = (e) => console.log(e.data);  // [1, 4, 9, 16]

// Clean up the blob URL when done
// URL.revokeObjectURL(blob URL);

// Useful for: single-file demos, bundlers that inline workers,
// or when you can't serve a separate .js file

Transferable Objects (Zero-Copy)

Normalmente, postMessage copia dados (structured clone), o que é lento para buffers grandes. A transfer list (segundo argumento) MOVE a propriedade de ArrayBuffer/MessagePort/ImageBitmap para o worker com zero cópia — quase instantâneo independente do tamanho. O buffer original torna-se detached (inutilizável). Use isso para grandes datasets, processamento de imagem ou áudio para evitar a sobrecarga de cópia.

javascript
// Transferable objects move data to a worker WITHOUT copying
// (the original becomes unusable) — much faster for large ArrayBuffers

// Create a large buffer
const buffer = new ArrayBuffer(1024 * 1024 * 10);  // 10MB
const view = new Float64Array(buffer);
view[0] = 3.14;

// Transfer the buffer (second arg = transfer list)
worker.postMessage({ data: buffer }, [buffer]);

// After transfer, 'buffer' is detached (length becomes 0)
console.log(buffer.byteLength);  // 0 — ownership moved to worker

// Worker receives it normally:
// self.onmessage = (e) => {
//   const buf = e.data.data;  // full 10MB, no copy
// };

// Transferable types: ArrayBuffer, MessagePort, ImageBitmap
// Use for: large datasets, image data, audio buffers

SharedArrayBuffer & Atomics

SharedArrayBuffer permite memória compartilhada verdadeira entre threads (sem cópia), e Atomics fornece operações thread-safe (add, load, store, compareExchange, wait/notify) nela. Isso habilita algoritmos paralelos de alta performance no JS. Devido a preocupações de segurança Spectre, SharedArrayBuffer requer headers HTTP de cross-origin isolation — sem eles, está desabilitado em navegadores modernos. Use para interop com WASM e computação paralela pesada.

javascript
// SharedArrayBuffer: memory shared between main thread and workers
// (both can read/write simultaneously) — true shared memory

// const sharedBuffer = new SharedArrayBuffer(1024);
// const view = new Int32Array(sharedBuffer);
// worker.postMessage({ buffer: sharedBuffer });

// Atomics: thread-safe operations on SharedArrayBuffer
// const sharedArray = new Int32Array(sharedBuffer);
// Atomics.add(sharedArray, 0, 1);     // atomic increment
// Atomics.load(sharedArray, 0);       // atomic read
// Atomics.store(sharedArray, 0, 42);  // atomic write
// Atomics.compareExchange(sharedArray, 0, 42, 99);  // CAS

// Atomics.wait / notify: block and wake threads
// Worker:
//   Atomics.wait(sharedArray, 0, 0);  // block until index 0 != 0
// Main:
//   Atomics.store(sharedArray, 0, 1);
//   Atomics.notify(sharedArray, 0);   // wake waiting workers

// NOTE: Requires cross-origin isolation headers:
//   Cross-Origin-Opener-Policy: same-origin
//   Cross-Origin-Embedder-Policy: require-corp

Padrão Worker Pool

Criar workers tem sobrecarga, então um worker pool reutiliza um número fixo de workers e enfileira tarefas — como um thread pool em outras linguagens. Esse padrão maximiza a utilização de CPU (um worker por core) enquanto evita o custo de spawn de workers por tarefa. O pool despacha tarefas para workers livres e as enfileira quando todos estão ocupados. Essencial para processar muitos chunks independentes de trabalho.

javascript
// A worker pool reuses workers to avoid creation overhead
class WorkerPool {
  constructor(workerUrl, size = 4) {
    this.workers = [];
    this.queue = [];
    for (let i = 0; i < size; i++) {
      const worker = new Worker(workerUrl);
      worker.busy = false;
      this.workers.push(worker);
    }
  }

  run(data) {
    return new Promise((resolve, reject) => {
      const task = { data, resolve, reject };
      const worker = this.workers.find(w => !w.busy);
      if (worker) {
        this.execute(worker, task);
      } else {
        this.queue.push(task);  // wait for a free worker
      }
    });
  }

  execute(worker, task) {
    worker.busy = true;
    worker.onmessage = (e) => {
      worker.busy = false;
      task.resolve(e.data);
      this.next();
    };
    worker.postMessage(task.data);
  }

  next() {
    const task = this.queue.shift();
    const worker = this.workers.find(w => !w.busy);
    if (task && worker) this.execute(worker, task);
  }
}
21

Manipulação Profunda do DOM

querySelector

querySelector retorna a primeira correspondência, querySelectorAll retorna uma NodeList estática. getElementsByClassName retorna uma HTMLCollection live. NodeList suporta forEach; HTMLCollection não.

javascript
const el = document.querySelector('.my-class');
const all = document.querySelectorAll('div.item');
all.forEach(el => console.log(el.textContent));
const live = document.getElementsByClassName('item');  // live
const static = document.querySelectorAll('.item');     // static

Criar & Inserir

createElement cria um novo elemento. appendChild adiciona como último child, prepend adiciona como primeiro. textContent é mais seguro que innerHTML pois previne XSS.

javascript
const div = document.createElement('div');
div.className = 'card';
div.textContent = 'Hello';
div.setAttribute('data-id', '42');
const parent = document.querySelector('#container');
parent.appendChild(div);
parent.prepend(div);

Event Delegation

Event delegation anexa um listener a um parent em vez de muitos a children. closest encontra o ancestral mais próximo que corresponde a um seletor. Lida com elementos adicionados dinamicamente.

javascript
document.querySelector('#list').addEventListener('click', (e) => {
    const item = e.target.closest('.list-item');
    if (!item) return;
    console.log('Clicked:', item.dataset.id);
});

API classList

classList fornece métodos para manipular classes CSS com segurança. toggle retorna true se adicionado, false se removido. Mais limpo que manipular a string className.

javascript
const el = document.querySelector('.box');
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('dark-mode');
el.classList.replace('old', 'new');
if (el.classList.contains('active')) { /* ... */ }

Atributos Dataset

atributos data-* armazenam dados personalizados. dataset fornece acesso camelCase: data-user-id torna-se dataset.userId. Valores são sempre strings.

javascript
// HTML: <div data-user-id="42" data-role="admin"></div>
const el = document.querySelector('[data-user-id]');
console.log(el.dataset.userId);  // "42"
console.log(el.dataset.role);    // "admin"
el.dataset.userId = '99';
22

Canvas API

Desenho Básico

getContext("2d") retorna o contexto de renderização 2D. fillRect desenha um retângulo preenchido, strokeRect desenha um contorno. Defina fillStyle/strokeStyle antes de desenhar.

javascript
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 50);
ctx.strokeStyle = 'blue';
ctx.lineWidth = 3;
ctx.strokeRect(130, 10, 100, 50);

Paths & Linhas

moveTo posiciona o cursor sem desenhar. lineTo desenha uma linha. closePath conecta de volta ao início. arc desenha círculos/arcos.

javascript
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.lineTo(100, 150);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(250, 35, 25, 0, Math.PI * 2);
ctx.fill();

Texto & Gradientes

createLinearGradient cria um gradiente. addColorStop define cores em posições (0-1). Defina font e textAlign antes de desenhar texto.

javascript
const grad = ctx.createLinearGradient(0, 0, 200, 0);
grad.addColorStop(0, 'red');
grad.addColorStop(1, 'blue');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 100);
ctx.font = '24px Arial';
ctx.fillText('Hello Canvas', 100, 50);

Loop de Animação

requestAnimationFrame sincroniza com a atualização do display (~60fps). clearRect limpa antes de cada frame. Cancele com cancelAnimationFrame.

javascript
let x = 0;
function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'green';
    ctx.fillRect(x, 50, 30, 30);
    x += 2;
    if (x > canvas.width) x = 0;
    requestAnimationFrame(animate);
}
animate();

Manipulação de Imagem

drawImage renderiza imagens. getImageData retorna dados de pixel como arrays RGBA. Manipule pixels para filtros. putImageData escreve pixels modificados de volta.

javascript
const img = new Image();
img.onload = () => {
    ctx.drawImage(img, 0, 0, 200, 150);
    const data = ctx.getImageData(0, 0, 200, 150);
    for (let i = 0; i < data.data.length; i += 4)
        data.data[i] = 255 - data.data[i];
    ctx.putImageData(data, 0, 0);
};
img.src = 'photo.jpg';
23

WebSockets

WebSocket Básico

WebSocket fornece comunicação full-duplex sobre uma única conexão TCP. onopen dispara quando conectado, onmessage quando dados chegam. Sempre trate onerror.

javascript
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => { ws.send('Hello Server'); };
ws.onmessage = (e) => { console.log('Received:', e.data); };
ws.onclose = () => console.log('Disconnected');
ws.onerror = (err) => console.error('Error:', err);

Enviar & Receber JSON

Dados WebSocket são transmitidos como strings ou binários. JSON.stringify/parse habilita troca de dados estruturados. Um campo type habilita roteamento de mensagens.

javascript
ws.onopen = () => {
    ws.send(JSON.stringify({ type: 'message', text: 'Hello' }));
};
ws.onmessage = (e) => {
    const data = JSON.parse(e.data);
    switch (data.type) {
        case 'message': console.log(data.text); break;
    }
};

Reconexão

Conexões WebSocket podem cair. Backoff exponencial (2^retries) previne sobrecarregar o servidor. Limite o delay a 30s. Reset a contagem de retry em caso de sucesso.

javascript
class ReconnectingWS {
    constructor(url) { this.url = url; this.retries = 0; this.connect(); }
    connect() {
        this.ws = new WebSocket(this.url);
        this.ws.onopen = () => { this.retries = 0; };
        this.ws.onclose = () => {
            const delay = Math.min(1000 * 2 ** this.retries++, 30000);
            setTimeout(() => this.connect(), delay);
        };
    }
}

Dados Binários

WebSocket suporta dados binários via ArrayBuffer. Defina binaryType para arraybuffer. DataView fornece acesso tipado. Binário é mais eficiente para dados numéricos.

javascript
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt32(0, 42);
ws.send(buffer);
ws.binaryType = 'arraybuffer';
ws.onmessage = (e) => {
    if (e.data instanceof ArrayBuffer) {
        const v = new DataView(e.data);
        console.log(v.getInt32(0));
    }
};

Heartbeat

Heartbeats detectam conexões stale. Envie pings e espere pongs. Se nenhum pong dentro do timeout, feche e reconecte. readyState verifica status da conexão.

javascript
setInterval(() => {
    if (ws.readyState === WebSocket.OPEN)
        ws.send(JSON.stringify({ type: 'ping' }));
}, 30000);
setInterval(() => {
    if (Date.now() - lastPong > 60000) ws.close();
}, 10000);
24

Service Workers

Registro

Service workers rodam em uma thread separada, interceptando requisições de rede. O registro deve acontecer em HTTPS ou localhost. A localização do arquivo SW determina o scope.

javascript
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js')
        .then(reg => console.log('Registered:', reg.scope))
        .catch(err => console.error('Failed:', err));
}

Caching

Estratégia cache-first: sirva do cache, fallback para rede. install pré-armazena assets em cache. fetch intercepta requisições. Outras estratégias: network-first, stale-while-revalidate.

javascript
const CACHE = 'app-v1';
self.addEventListener('install', (e) => {
    e.waitUntil(caches.open(CACHE)
        .then(c => c.addAll(['/', '/index.html', '/style.css'])));
});
self.addEventListener('fetch', (e) => {
    e.respondWith(caches.match(e.request)
        .then(cached => cached || fetch(e.request)));
});

Background Sync

Background sync adia ações até a conectividade retornar. O evento sync dispara quando a rede está disponível. Armazene ações pendentes no IndexedDB.

javascript
navigator.serviceWorker.ready.then(reg =>
    reg.sync.register('send-messages'));
self.addEventListener('sync', (e) => {
    if (e.tag === 'send-messages')
        e.waitUntil(sendPendingMessages());
});

Push Notifications

Push notifications funcionam mesmo quando o app está fechado. subscribe registra com um push service usando chaves VAPID. userVisibleOnly requer mostrar uma notificação.

javascript
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: vapidPublicKey
});
self.addEventListener('push', (e) => {
    const data = e.data.json();
    e.waitUntil(self.registration.showNotification(data.title, {
        body: data.body
    }));
});

Atualizar & Ativar

skipWaiting ativa um novo SW imediatamente. activate limpa caches antigos. clients.claim assume controle imediatamente. Versione o nome do cache para acionar atualizações.

javascript
self.addEventListener('install', (e) => {
    self.skipWaiting();
});
self.addEventListener('activate', (e) => {
    e.waitUntil(caches.keys().then(keys =>
        Promise.all(keys.filter(k => k !== CACHE)
            .map(k => caches.delete(k)))));
    self.clients.claim();
});
25

IndexedDB

Abrir Banco de Dados

IndexedDB é um banco de dados NoSQL no navegador. onupgradeneeded dispara quando a versão muda, usado para criar object stores. keyPath define a chave primária.

javascript
const req = indexedDB.open('MyDatabase', 1);
req.onupgradeneeded = (e) => {
    const db = e.target.result;
    if (!db.objectStoreNames.contains('users'))
        db.createObjectStore('users', { keyPath: 'id' });
};
req.onsuccess = (e) => { const db = e.target.result; };

Add & Put

Transações agrupam operações atomicamente. add falha em chaves duplicadas, put sobrescreve. readwrite permite modificações. oncomplete dispara em caso de sucesso.

javascript
const tx = db.transaction('users', 'readwrite');
const store = tx.objectStore('users');
store.add({ id: 1, name: 'Alice' });  // Fails if key exists
store.put({ id: 1, name: 'Bob' });   // Overwrites
tx.oncomplete = () => console.log('Saved');

Consultar Dados

get recupera por chave. openCursor itera registros. cursor.continue move para o próximo. Envolva em Promises para uso com async/await.

javascript
const tx = db.transaction('users', 'readonly');
const req = tx.objectStore('users').get(1);
req.onsuccess = () => console.log(req.result);
const cursorReq = tx.objectStore('users').openCursor();
cursorReq.onsuccess = (e) => {
    const cursor = e.target.result;
    if (cursor) { console.log(cursor.value); cursor.continue(); }
};

Consultas por Index

Indexes habilitam consultas em campos não-chave. IDBKeyRange cria bounds. Indexes devem ser criados em onupgradeneeded.

javascript
const tx = db.transaction('users', 'readonly');
const index = tx.objectStore('users').index('name');
index.get('Alice').onsuccess = (e) =>
    console.log(e.target.result);
const range = IDBKeyRange.bound('A', 'M');
index.openCursor(range).onsuccess = (e) => {
    const cursor = e.target.result;
    if (cursor) { cursor.continue(); }
};

Deletar & Limpar

delete remove um único registro. clear remove todos os registros. deleteDatabase remove todo o banco de dados. Todas as modificações requerem transações readwrite.

javascript
const tx = db.transaction('users', 'readwrite');
tx.objectStore('users').delete(1);  // Delete by key
tx.objectStore('users').clear();   // Clear all
indexedDB.deleteDatabase('MyDatabase');  // Delete DB
tx.oncomplete = () => console.log('Done');
26

WebRTC

Get User Media

getUserMedia solicita acesso à câmera e microfone. Retorna um MediaStream. srcObject atribui o stream ao elemento de vídeo. Requer HTTPS e permissão do usuário.

javascript
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
    .then(stream => {
        document.querySelector('video').srcObject = stream;
    });

Peer Connection

RTCPeerConnection estabelece conexões P2P. addTrack adiciona mídia. ontrack recebe o stream remoto. ICE candidates são caminhos de rede trocados via signaling.

javascript
const pc = new RTCPeerConnection(config);
stream.getTracks().forEach(t => pc.addTrack(t, stream));
pc.ontrack = (e) => { remoteVideo.srcObject = e.streams[0]; };
pc.onicecandidate = (e) => {
    if (e.candidate) sendToPeer(e.candidate);
};

Offer & Answer

Negociação SDP: troca offer/answer descreve formatos de mídia. setLocalDescription define o SDP local, setRemoteDescription define o SDP remoto.

javascript
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
sendToPeer(offer);
// Callee:
await pc.setRemoteDescription(offer);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
sendToPeer(answer);

Data Channels

Data channels habilitam transferência arbitrária de dados sobre WebRTC com baixa latência. createDataChannel cria no offerer. ondatachannel recebe no answerer.

javascript
const channel = pc.createDataChannel('chat');
channel.onopen = () => console.log('Connected');
channel.onmessage = (e) => console.log('Received:', e.data);
channel.send('Hello peer!');
pc.ondatachannel = (e) => {
    e.channel.onmessage = (ev) => console.log(ev.data);
};

Compartilhamento de Tela

getDisplayMedia captura tela, janela ou aba. O navegador mostra um seletor. onended dispara quando o usuário para de compartilhar. Sempre trate a limpeza.

javascript
const stream = await navigator.mediaDevices.getDisplayMedia({
    video: { frameRate: 30 }, audio: true
});
video.srcObject = stream;
stream.getVideoTracks()[0].onended = () =>
    console.log('Stopped');
27

Otimização de Performance

Debounce & Throttle

Debounce atrasa a execução até as chamadas pararem (bom para busca). Throttle limita a uma vez por intervalo (bom para scroll). Ambos previnem chamadas excessivas.

javascript
function debounce(fn, delay) {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => fn(...args), delay);
    };
}
function throttle(fn, limit) {
    let inThrottle;
    return (...args) => {
        if (!inThrottle) { fn(...args); inThrottle = true;
            setTimeout(() => inThrottle = false, limit); }
    };
}

Web Workers

Web Workers executam JavaScript em uma thread separada para tarefas CPU-intensive. Dados são passados via postMessage. Workers não podem acessar o DOM.

javascript
const worker = new Worker('worker.js');
worker.postMessage({ data: [1, 2, 3] });
worker.onmessage = (e) => console.log('Result:', e.data);
// worker.js
self.onmessage = (e) => {
    self.postMessage(e.data.data.map(x => x ** 2));
};

Lazy Loading

IntersectionObserver dispara quando elementos entram no viewport. data-src contém a URL real; src é definido quando visível. Reduz o carregamento inicial da página para páginas com muitas imagens.

javascript
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            observer.unobserve(img);
        }
    });
});
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));

Atualizações em Lote do DOM

DocumentFragment agrupa inserções de DOM em um único reflow, muito mais rápido que anexar uma por uma. Minimize layout thrashing.

javascript
const fragment = document.createDocumentFragment();
items.forEach(item => {
    const li = document.createElement('li');
    li.textContent = item;
    fragment.appendChild(li);
});
list.appendChild(fragment);  // Single reflow

Gerenciamento de Memória

WeakMap permite GC de chaves, prevenindo leaks. Sempre remova event listeners quando elementos são removidos. Defina objetos grandes como null para GC.

javascript
const cache = new WeakMap();
cache.set(element, data);
// When element is GC'd, entry is removed
element.removeEventListener('click', handler);
bigArray = null;  // Allow GC
28

Segurança (XSS/CSRF)

Prevenção de XSS

XSS injeta scripts maliciosos via entrada do usuário. Nunca use innerHTML com dados não confiáveis. textContent é seguro. Headers CSP restringem fontes de script.

javascript
// BAD: vulnerable to XSS
element.innerHTML = userInput;
// GOOD: safe alternatives
element.textContent = userInput;
function escapeHtml(str) {
    const div = document.createElement('div');
    div.textContent = str;
    return div.innerHTML;
}

Prevenção de CSRF

CSRF engana usuários para ações indesejadas. Tokens garantem que requisições vieram do seu app. Cookies SameSite=Strict não são enviados cross-site. Use CSRF tokens para operações que mudam estado.

javascript
const token = document.cookie.match(/csrf_token=([^;]+)/)?.[1];
fetch('/api/data', {
    method: 'POST',
    headers: { 'X-CSRF-Token': token }
});
// SameSite cookie: Set-Cookie: session=abc; SameSite=Strict

Content Security Policy

CSP restringe quais recursos podem carregar. default-src é o fallback. script-src controla JavaScript. Comece com modo report-only antes de aplicar.

javascript
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self';
               script-src 'self' https://cdn.example.com;
               style-src 'self' 'unsafe-inline';
               img-src 'self' data:">

Cookies Seguros

HttpOnly previne acesso JavaScript a cookies, mitigando XSS. Secure garante apenas HTTPS. SameSite=Strict previne CSRF. Cookies de sessão devem sempre usar ambos.

javascript
// Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
// HttpOnly: not accessible via JavaScript
// Secure: only sent over HTTPS
// SameSite: Strict | Lax | None
document.cookie  // Cannot see HttpOnly cookies

Validação de Entrada

Validação client-side melhora UX, mas não é segurança. Sempre valide no servidor. Use DOMPurify para sanitização de HTML. Whitelist de tags permitidas.

javascript
function validateEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function sanitize(html) {
    return DOMPurify.sanitize(html, { ALLOWED_TAGS: ['p', 'br', 'strong'] });
}
// Never trust client input - always validate on server too
29

Padrões de Design

Singleton

Singleton garante que apenas uma instância exista. O construtor retorna a instância existente. Útil para configuração, logging e conexões de banco de dados.

javascript
class Config {
    constructor() {
        if (Config.instance) return Config.instance;
        this.settings = {};
        Config.instance = this;
    }
}
const c1 = new Config();
const c2 = new Config();
console.log(c1 === c2);  // true

Observer/Pub-Sub

Padrão Observer permite que objetos se inscrevam em eventos. on registra, emit aciona, off cancela inscrição. Fundamento de arquiteturas event-driven.

javascript
class EventEmitter {
    constructor() { this.events = {}; }
    on(event, cb) { (this.events[event] ||= []).push(cb); }
    emit(event, data) { (this.events[event] || []).forEach(cb => cb(data)); }
    off(event, cb) { this.events[event] = this.events[event]?.filter(c => c !== cb); }
}

Factory

Factory cria objetos sem expor a lógica de instanciação. O chamador especifica um tipo, a factory decide qual classe instanciar.

javascript
class Dog { speak() { return 'Woof'; } }
class Cat { speak() { return 'Meow'; } }
function AnimalFactory(type) {
    switch (type) {
        case 'dog': return new Dog();
        case 'cat': return new Cat();
    }
}

Padrão Module

O padrão module encapsula estado privado usando closures. IIFE cria um escopo privado. Apenas métodos retornados são públicos.

javascript
const counter = (() => {
    let count = 0;  // Private
    return {
        increment: () => ++count,
        getCount: () => count
    };
})();
counter.increment();
console.log(counter.getCount());  // 1

Strategy

Padrão Strategy encapsula algoritmos intercambiáveis. O contexto delega para a estratégia selecionada. Evita grandes cadeias if/else.

javascript
const strategies = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
};
function calculate(strategy, a, b) {
    return strategies[strategy]?.(a, b);
}
30

Armadilhas Comuns

Binding de this

this é determinado por como uma função é chamada. Arrow functions herdam this do escopo envolvente. Métodos perdem this quando separados. Use bind.

javascript
const obj = {
    name: 'Alice',
    greet: function() { console.log(this.name); },
    arrow: () => console.log(this.name)
};
obj.greet();   // Alice
obj.arrow();   // undefined
const fn = obj.greet;
fn();  // undefined (lost binding)

Ponto Flutuante

JavaScript usa ponto flutuante IEEE 754. Use Number.EPSILON para comparações, ou multiplique por potências de 10 para trabalhar com inteiros.

javascript
console.log(0.1 + 0.2);        // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
const eps = Number.EPSILON;
console.log(Math.abs(0.1 + 0.2 - 0.3) < eps);  // true

== vs ===

== faz coerção de tipo, levando a resultados surpreendentes. === verifica tanto tipo quanto valor sem coerção. Sempre use === e !==.

javascript
console.log(0 == false);      // true
console.log('' == false);     // true
console.log(null == undefined); // true
console.log(0 === false);     // false
console.log('' === false);    // false
// Always use ===

Closures em Loops

var é function-scoped, então todas as closures compartilham a mesma variável. let é block-scoped, criando um novo binding por iteração. Sempre use let/const.

javascript
// BUG: all print 3
for (var i = 0; i < 3; i++)
    setTimeout(() => console.log(i), 100);
// FIX: let
for (let i = 0; i < 3; i++)
    setTimeout(() => console.log(i), 100);

Tratamento de Erros Async

Promise rejections não tratadas podem travar o Node.js. Sempre envolva await em try/catch, ou use .catch(). Escute eventos unhandledRejection.

javascript
// BUG: unhandled rejection
async function fetchData() {
    const data = await fetch('/api');
    return data.json();
}
// FIX: try/catch
async function fetchDataSafe() {
    try { return await (await fetch('/api')).json(); }
    catch (err) { console.error('Failed:', err); return null; }
}

Snippets de JavaScript relacionados

Copy-paste ready code for common tasks.

Array Map Filter Reduce

Encadear map, filter, reduce, find, some e every em arrays.

Deduplicação de Array

Deduplicar um array usando Set.

Clone Profundo

Clonar profundamente objetos, suportando tipos de dados comuns.

Função Debounce

Esperar um período de tempo após um evento disparar antes de executar; reiniciar o timer se disparado novamente durante a espera.

Função Throttle

Limitar uma função a executar no máximo uma vez dentro de um intervalo de tempo.

Controle de Concorrência Promise.all

Um executor de Promise com limite de concorrência.

Tratamento de Erros async/await

Envolver funções async para capturar exceções uniformemente.

Wrapper de Fetch

Envolver fetch com timeout, tratamento de erros e análise JSON.

Operações de localStorage

Envolver localStorage com tempo de expiração e suporte JSON.

Operações de Cookie

Envolver operações de leitura, escrita e exclusão de Cookie.

Análise de Parâmetros de URL

Analisar query string de URL em um objeto.

Formatação de Data

Formatar uma data em uma string especificada.

Formatação de Moeda

Formatar um número como uma string de moeda separada por milhares.

Geração de Números Aleatórios

Gerar números aleatórios e strings aleatórias dentro de um intervalo.

Conversão de Cores

Converter entre cores RGB e HEX.

Geração de UUID

Gerar um identificador único em conformidade com UUID v4.

Truncamento de String

Truncar uma string e anexar reticências.

Achatamento de Array

Achatar um array multidimensional em uma dimensão.

Merge de Objetos

Mesclar profundamente múltiplos objetos.

Verificação de Tipo

Determinar precisamente tipos de dados JavaScript.

Delegação de Eventos

Implementar delegação de eventos via event bubbling.

Manipulação de DOM

Criar e manipular dinamicamente elementos DOM.

Validação de Formulário

Uma coleção de regras comuns de validação de formulário.

Upload de Arquivo

Envolver upload de arquivo com suporte a progresso e chunking.

Lazy Loading de Imagem

Implementar lazy loading de imagem com IntersectionObserver.

Copiar para Área de Transferência

Um método de cópia para área de transferência cross-browser.

API de Fullscreen

Envolver operações de fullscreen do navegador.

Geolocalização

Obter informações de geolocalização do usuário.

Web Worker

Criar um Web Worker para executar tarefas demoradas.

Service Worker

Registrar um Service Worker para cache offline.

Operações de IndexedDB

Envolver operações CRUD do IndexedDB.

Desenho em Canvas

Exemplo básico de desenho em Canvas.

Was this helpful?

Learning path

Learn from scratch

Learn this language from the ground up with structured lessons.