Teu progresso
0 / 83 módulos0%
Estágio 01 · 01-07
BloqueadoJavaScript é a linguagem mais usada no mundo, e a mais mal-entendida: porque a maioria dos devs aprende sintaxe sem nunca olhar o que está embaixo. Você usa Promise, async/await, setTimeout, closure, this, class, mas se eu te perguntar:
setTimeout(fn, 0) ou Promise.resolve().then(fn)?"this aponta dentro de um arrow function?"[1,2,3] + [4,5,6] retorna '1,2,34,5,6'?"Sem respostas precisas a essas perguntas, você não domina JavaScript. E como JS sustenta Node, React, Next.js, todo seu stack daqui pra frente vira castelo na areia.
Este módulo te dá o JS de verdade. Depois dele, TypeScript (01-08) vira evolução natural.
O V8 (engine do Chrome/Node) executa JS em 5 passos simplificados:
Hidden classes (shapes): quando V8 vê um objeto, cria uma "shape" descrevendo seu layout (offsets das propriedades). Objetos com mesma shape compartilham. Adicionar/remover propriedade muda a shape → invalida hidden class → invalida código otimizado.
Lição prática:
Inline caches (ICs): ao chamar obj.foo, V8 cacheia "shape do obj + offset do foo". Próximas chamadas com mesma shape são quase grátis. Diferentes shapes → IC vira "polymorphic" (3-4 shapes) → "megamorphic" (>4) → cai pra slow path.
GC (Garbage Collector) do V8, generational:
Implicação: quanto mais "lixo" você gera (alocações temporárias), mais GC roda. Profile com --trace-gc se suspeitar.
Primitivos (passados por valor): number, string, boolean, null, undefined, bigint, symbol.
Reference (passados por referência): object, array, function (que é object).
let a = 1; let b = a; b = 2; // a = 1, b = 2
let x = {n:1}; let y = x; y.n = 2; // x.n = 2, y.n = 2 (mesma ref)
Strings são imutáveis (não pode mudar caractere por índice). "abc"[0] = "z" falha silenciosamente em sloppy mode, throw em strict.
Number em JS é IEEE 754 double-precision (64-bit float). Por isso 0.1 + 0.2 !== 0.3. Pra dinheiro, use bigint ou strings ou libs (big.js, decimal.js).
BigInt (ES2020): inteiros arbitrariamente grandes. 100n. Não interopera com number direto.
Symbol: identificador único. Symbol('foo') !== Symbol('foo'). Usado pra propriedades não-enumeráveis, como Symbol.iterator.
JS converte tipos automaticamente em muitos contextos. Regras precisas estão na spec ECMA-262, seção ToPrimitive/ToNumber/ToString.
== vs ===:
=== (strict): tipo igual, valor igual. Use sempre.== (loose): tenta coercion antes. Inferno em casos: null == undefined (true), 0 == '' (true), [] == false (true).+ com string: concatena. Senão soma.
1 + 1 // 2
1 + '1' // '11'
1 + null // 1 (null → 0)
1 + undefined // NaN (undefined → NaN)
[] + [] // '' (each → '')
[] + {} // '[object Object]'
[1,2,3] + [4,5,6] → ToPrimitive([1,2,3]) = "1,2,3"; mesma coisa pro outro; concatena → "1,2,34,5,6". Spec literal.
Truthy/falsy:
false, 0, -0, 0n, '', null, undefined, NaN. Tudo.[], {}, '0', 'false').Booleanização:
if (x), !!x, Boolean(x) → todas usam ToBoolean.[] é truthy. Boolean([]) = true. if ([]) executa o if.Escopo léxico: o escopo de uma variável é determinado por onde ela é definida no código, não onde é chamada.
const x = 10;
function outer() {
const y = 20;
function inner() {
return x + y; // ambos visíveis: léxico
}
return inner;
}
const fn = outer();
fn(); // 30, mesmo que `outer` já retornou, `inner` capturou o ambiente
Closure = função + ambiente léxico capturado.
Esse é o motor de:
const counter = (() => {
let count = 0;
return { inc: () => ++count, get: () => count };
})();
useState retorna ref ao state guardado no fiber via closure interna.Bug clássico do var em loop:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// imprime: 3, 3, 3, todas as closures compartilham `i` (function-scoped)
Soluções:
let i (block-scoped → cada iteração tem novo i na closure).setTimeout((function(j){ return () => console.log(j); })(i), 0);Array.from/forEach com index.this, o bicho de 7 cabeçasthis é o contexto de uma chamada. Determinado por como a função é chamada, não onde definida.
| Forma de chamada | this |
|---|---|
obj.method() | obj |
fn() (standalone) | undefined (strict) ou globalThis (sloppy) |
new Constructor() | objeto novo |
fn.call(ctx), fn.apply(ctx, args) | ctx |
fn.bind(ctx)() | ctx (fixo) |
| Arrow function | this do escopo léxico (não tem próprio) |
Arrow functions não têm this, arguments, super, new.target próprios. Capturam do escopo. Por isso setInterval(() => this.tick(), 1000) dentro de classe funciona sem bind.
Métodos passados como callback perdem this:
class C {
constructor() { this.n = 1; }
m() { console.log(this.n); }
}
const c = new C();
const f = c.m;
f(); // TypeError: this is undefined (strict) ou this.n é window.n
Solução: bind(c) ou usar arrow.
JS é prototype-based, não class-based. Classes ES6 são sintaxe sobre prototypes.
Cada objeto tem um [[Prototype]] interno (acessível via Object.getPrototypeOf(obj) ou legacy obj.__proto__). Quando você acessa obj.foo:
obj mesmo.[[Prototype]].null.function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { return this.name; };
const dog = new Animal('Rex');
dog.speak(); // procura speak em dog → não acha → procura em Animal.prototype → acha
new Constructor() em 4 passos:
[[Prototype]] = Constructor.prototype.Constructor.call(novoObj, ...args).Class ES6 desugaring:
class Dog extends Animal {
bark() { return 'woof'; }
}
// equivale a:
function Dog(...args) { Animal.call(this, ...args); }
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() { return 'woof'; };
instanceof anda na chain procurando Class.prototype.
Object.create(proto) cria objeto com [[Prototype]] específico, útil pra criar inheritance "manual".
JavaScript é single-threaded mas assíncrono. Como?
Conceitos:
setTimeout, setInterval, setImmediate (Node), I/O callback vira task.Promise.then/catch/finally, queueMicrotask, MutationObserver (browser).Loop:
while (true) {
// 1. Se stack não vazia: executa próxima instrução (sync)
// 2. Se stack vazia:
// a. Drena microtask queue COMPLETAMENTE (pode adicionar mais microtasks; processe todas)
// b. Pega 1 macrotask, executa até esvaziar stack
// c. (browser) Render se necessário
// 3. Se nada a fazer: bloqueia em I/O multiplex (epoll/kqueue/IOCP)
}
Ordem clássica:
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// Saída: 1, 4, 3, 2
1, 4 síncronos primeiro.3.2.No Node, há fases (timers, pending, idle/prepare, poll, check, close). setImmediate é macrotask na fase check. process.nextTick tem fila própria, antes de microtasks. Evite process.nextTick recursivo (starva o loop).
Promise é objeto com 3 estados: pending, fulfilled, rejected. Transição é única e final.
const p = new Promise((resolve, reject) => {
// executor roda IMEDIATAMENTE (síncrono)
setTimeout(() => resolve(42), 100);
});
p.then(v => console.log(v)); // microtask quando resolver
then(onFulfilled, onRejected) retorna nova Promise representando o resultado da callback.
async/await é sintaxe sobre Promise + generators:
async function fn() {
const x = await promise1;
return x + 1;
}
// Equivale a:
function fn() {
return promise1.then(x => x + 1);
}
Cuidado: await em loop sequencializa.
// SEQUENCIAL, lento se calls são independentes:
for (const url of urls) await fetch(url);
// PARALELO:
await Promise.all(urls.map(u => fetch(u)));
Iterator protocol:
const iter: Iterator<number> = {
i: 0,
next() {
return { value: this.i++, done: this.i > 3 };
},
};
for...of consome iterators. Arrays, Maps, Sets, strings são iterables (têm [Symbol.iterator]).
Generators:
function* counter() {
yield 1;
yield 2;
yield 3;
}
const g = counter();
g.next(); // { value: 1, done: false }
Generators pausam em yield e retomam em next(). Permite lazy sequences, control flow customizado, schedulers.
JS é managed. Você não dá free. Mas leaks acontecem:
el.addEventListener(...) sem removeEventListener.setInterval sem clearInterval.Map que cresce indefinidamente.Ferramentas: Chrome DevTools Memory tab, heap snapshot, allocation timeline.
WeakMap / WeakSet: keys são fracas, não impedem GC. Útil pra cache associada a objetos.
WeakRef (ES2021): referência fraca a objeto, GC pode coletar. Use com FinalizationRegistry pra cleanup.
ES Modules (ESM) desde 2015:
// foo.mjs
export const x = 1;
export default function() {}
// bar.mjs
import { x } from './foo.mjs';
import fn from './foo.mjs';
ESM é estático (imports resolvidos no parse, não em runtime). Permite tree-shaking.
CommonJS (CJS): require/module.exports, é o modelo legacy do Node, dinâmico.
Em Node, package.json "type": "module" faz .js ser ESM. ESM importa CJS, mas CJS importa ESM só com await import() (dinâmico).
JavaScript de 2026 não é o de 2020. ES2024 e ES2025 entregaram features que extinguem categorias inteiras de utility code (lodash group/uniq, hand-rolled set ops, custom Date wrappers). Node 22 LTS (Out 2024) e Node 24 LTS (esperado Out 2026) trazem TypeScript stripping nativo, permission model, WebSocket built-in, require(esm) estável. V8 evoluiu pipeline (Ignition → Sparkplug → Maglev → TurboFan/TurboShaft). Quem ainda escreve código com padrões de 2018 paga em bytes, latência e bug surface.
Iterator helpers (TC39 Stage 4, ES2025; V8 13.0 Q4 2024). Métodos prototípicos em iteradores: .map(), .filter(), .take(), .drop(), .flatMap(), .reduce(), .toArray(), .forEach(), .some(), .every(). Diferença crítica vs Array: lazy. Chain de transformações não materializa intermediários — só consome quando o sink puxa.
// Materializa 3 arrays intermediários (1M elements cada) — O(n) memory
const result = Array.from({ length: 1_000_000 }, (_, i) => i)
.map(x => x * 2)
.filter(x => x % 3 === 0)
.slice(0, 10);
// Iterator helpers: lazy, zero arrays intermediários
function* range(n: number) { for (let i = 0; i < n; i++) yield i; }
const result2 = range(1_000_000)
.map(x => x * 2)
.filter(x => x % 3 === 0)
.take(10)
.toArray(); // só aqui materializa, com 10 elementos
Promise.withResolvers() (ES2024). Substitui o pattern imperativo de declarar resolve/reject fora do construtor:
// Antigo
let resolve: (v: string) => void, reject: (e: Error) => void;
const p = new Promise<string>((res, rej) => { resolve = res; reject = rej; });
// ES2024
const { promise, resolve, reject } = Promise.withResolvers<string>();
Object.groupBy() / Map.groupBy() (ES2024). Mata lodash.groupBy:
const orders = [{ region: "BR", total: 100 }, { region: "US", total: 50 }, { region: "BR", total: 200 }];
const byRegion = Object.groupBy(orders, o => o.region);
// { BR: [{...}, {...}], US: [{...}] }
Array.prototype.findLast() + findLastIndex() (ES2023, ubíquos em 2026).
RegExp /v flag (ES2024). Set notation, intersection, string properties:
/[\p{Letter}--\p{ASCII}]/v.test("ç"); // true (letras não-ASCII)
/[\p{Greek}&&\p{Letter}]/v.test("α"); // true (intersection)
/\p{RGI_Emoji}/v.test("🇧🇷"); // true (string property, multi-codepoint)
Set methods (TC39 Stage 4): .union(), .intersection(), .difference(), .symmetricDifference(), .isSubsetOf(), .isSupersetOf(), .isDisjointFrom(). Substitui hand-rolled com Array+filter+includes (O(n²)) por nativo (O(n)):
// Antigo: O(n*m) — filter+includes
const intersection = a.filter(x => b.includes(x));
// ES2025: O(n+m) — hash-backed
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);
const inter = a.intersection(b); // Set { 3, 4 }
const union = a.union(b); // Set { 1, 2, 3, 4, 5, 6 }
const diff = a.difference(b); // Set { 1, 2 }
Promise.try() (Stage 4). Wraps função sync ou async em Promise sem ramo extra:
// Antigo
Promise.resolve().then(() => fn(arg));
// ES2025
Promise.try(fn, arg);
Float16Array (Stage 4). Half-precision (16-bit) pra ML inference no browser, GPU shaders, audio buffers. Reduz memory 50% vs Float32Array com perda controlada.
Array.fromAsync() (ES2024 final). Coleta async iterable em array — útil pra Server-Sent Events, streams de arquivo, WebSocket buffers:
const lines = await Array.fromAsync(readSseStream(url));
Date é irreparável: timezone bugs, mutável, parser inconsistente. Temporal substitui com tipos imutáveis e timezone-aware. Types: Temporal.Instant, Temporal.PlainDate, Temporal.PlainTime, Temporal.PlainDateTime, Temporal.ZonedDateTime, Temporal.Duration. Polyfill @js-temporal/polyfill é maduro e production-ready (TC39 Temporal status 2024). Browser: Firefox em flag 2024, Safari TP 2024, Chromium em discussão 2026.
import { Temporal } from "@js-temporal/polyfill";
const delivery = Temporal.ZonedDateTime.from({
year: 2026, month: 5, day: 7, hour: 14, minute: 30,
timeZone: "America/Sao_Paulo",
});
const eta = delivery.add({ hours: 6, minutes: 15 });
const duration = eta.since(delivery); // Temporal.Duration
console.log(duration.toString()); // PT6H15M
using (Stage 4 — ES2024 explicit resource management)class DbConnection {
constructor() { /* open */ }
[Symbol.dispose]() { /* close socket */ }
query(sql: string) { /* ... */ }
}
{
using conn = new DbConnection();
conn.query("SELECT 1");
} // dispose() invocado automaticamente at scope exit, mesmo em throw
Async version: Symbol.asyncDispose + await using. TypeScript 5.2+ implementa types; Node 22+ runtime suporta. Hot use cases: DB connections, file handles, mutex locks, Kafka consumers, span tracers.
Fontes: Node 22 release notes (Out 2024), Node 22.12 changelog.
fetch GA nativo (estável desde Node 21, default em Node 22). Mata node-fetch, axios em scripts simples.--experimental-strip-types (Node 22+). Roda .ts direto sem tsc/tsx/ts-node — type annotations stripped. Útil pra scripts e prototyping.--env-file=.env (Node 20.6+, enriquecido em 22). Mata dotenv em scripts.node:test runner estável (Node 20.x → 22 LTS). node --test substitui Jest/Vitest pra suites simples — TAP output, paralelo, fixtures via before/after.--experimental-permission --allow-fs-read=/etc --allow-net=api.example.com. Sandbox-style; restringe blast radius.new WebSocket(url) sem ws package.require(esm) estável (Node 22.12+). require() carrega ESM síncrono — encerra dual-package hazard.# Roda TS direto + permission model em prod
node --experimental-strip-types \
--experimental-permission \
--allow-fs-read=/app/config \
--allow-net=api.stripe.com,api.cloudflare.com \
server.ts
Fontes: V8 blog "Maglev" (Ago 2023), V8 blog "TurboShaft" (Mai 2024).
Implicação prática: cold start reduzido (Sparkplug/Maglev fast tier-up); steady-state mantém ou melhora (TurboShaft). Afeta TTI, INP, throughput de servidor.
Bun 1.2 (changelog 2024-2025) é alternative production-ready a Node — bun install ~30x mais rápido que npm install, native bundler, native TypeScript runner, native test framework. Limitações: npm compat ~95%, alguns native modules (canvas, sharp antigo) quebram. Use cases: prototyping, CI runner (cold start fast), edge runtime. Não substitui Node em prod pra serviços B2B críticos sem CI compat test.
Standards-aligned (Web APIs primeiro). deno.json substitui package.json. deno install npm:react resolve npm. Built-in TypeScript, lint, fmt, test, coverage. Permission runtime nativo. Use cases: edge functions (Deno Deploy), serverless, scripting.
date-fns/dayjs são bridge pra deprecation.import { groupBy, uniq } from "lodash" — Object.groupBy, Set ops nativos.try/finally manual pra cleanup — using é declarativo e à prova de throw.tsx/ts-node em scripts simples — Node 22 --experimental-strip-types resolve.node:test/Vitest cobrem — Jest config overhead crescente, ESM ainda problemático.RegExp sem /u ou /v em 2026 — Unicode quebrado por padrão (surrogate pairs, \p{}).Array.from(generator()).map().filter() — materializa intermediários; usa Iterator helpers lazy.dotenv em scripts — Node 20.6+ tem --env-file nativo.fetch em Node — built-in desde Node 18, GA em 21.Backend Node 22 LTS roda --experimental-permission --allow-net=api.stripe.com,api.cloudflare.com --allow-fs-read=/app/config pra restringir blast radius de execução suspeita. Ingestor de GPS processa SSE stream com Iterator helpers (.take(), .filter(), .map()) lazy — zero intermediários em fluxo de 50k events/s. Kafka consumer usa await using consumer = await kafka.connect(...) (Symbol.asyncDispose cleanup automático em throw ou cancel). Datas de entrega em Temporal.ZonedDateTime com timezone explícito (cross-region: BR São Paulo, AR Buenos Aires, CL Santiago) — elimina classe inteira de bugs DST.
01-08 — TS type system, using/Symbol.dispose types em TS 5.2+.02-04 — React 19+ usa Iterator helpers internamente em transitions e Suspense.02-07 — Node.js internals, V8 pipeline e event loop interaction.03-02 — Docker base image Node 22 LTS slim/alpine.03-09 — frontend perf, V8 optimization tiers afetam INP e TTI.Pra passar o Portão Conceitual, sem consultar:
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
console.log(4);
var em loop e listar 3 soluções.[1,2,3] + [4,5,6] passo a passo (ToPrimitive → toString → concat).Dog extends Animal extends Object.this correspondente.await é desugarado pra .then.Map.Implementar MyPromise (Promise A+ compliant) do zero.
Você vai construir uma reimplementação de Promise que passa o test suite oficial Promises/A+ (promisesaplus.com).
MyPromise<T> com:
new MyPromise((resolve, reject) => ...).pending → fulfilled ou pending → rejected. Transição única, irreversível.then(onFulfilled?, onRejected?): retorna nova MyPromise. Se callback retorna valor → fulfill; retorna promise → unwrap; throw → reject.catch(onRejected): shorthand.finally(onFinally).then rodam em microtask (use queueMicrotask em Node ou MutationObserver no browser; não use setTimeout).MyPromise.resolve(value)MyPromise.reject(reason)MyPromise.all(iterable), todos resolvem ou primeiro reject.MyPromise.allSettled(iterable), espera todos, retorna array de {status, value/reason}.MyPromise.race(iterable), primeiro a settle (resolve ou reject).MyPromise.any(iterable), primeiro a resolve, ou AggregateError se todos reject.promises-aplus-tests com adapter (instruções em github.com/promises-aplus/promises-tests). Todos os testes têm que passar.strict: true).vitest + promises-aplus-tests pra rodar suite).setTimeout(0) pra microtask).[[Resolve]] (a parte mais sutil, quando callback retorna outra Promise/thenable, precisa unwrap recursivamente sem loop infinito)..then em chain.queueMicrotask e não setTimeout(0).AbortController / AbortSignal integration (cancelamento).for await ... of).epoll/kqueue/IOCP via libuv. Microtasks rodam em user space; macrotasks são frequentemente acionadas por syscalls completas.--inspect-brk + Chrome DevTools: debugger.--trace-gc: log de GC.--prof + --prof-process: profiling V8.src/builtins/, src/objects/.lib/internal/.Encerramento: 01-07 é o módulo que separa quem escreve JS de quem entende JS. Depois dele, frameworks deixam de ser caixas mágicas. Você passa a ler código de libs, debug em DevTools, e otimizar V8 com base em sinais reais.
Threshold de Maestria
Acerte todas as 5 pra marcar o módulo como concluído. Sem pressa, sem timer. Tudo fica salvo no teu navegador.
Q1Qual a saída exata e por quê? console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); console.log(4);
Q2Por que mudar a 'shape' de um objeto em runtime degrada performance no V8?
Q3Qual o resultado deste código e como corrigir? for (var i = 0; i < 3; i++) setTimeout(() => console.log(i), 0);
Q4Em ES2025+, qual a diferença prática entre [...arr].map(...).filter(...).slice(0, 10) vs arr.values().map(...).filter(...).take(10).toArray() pra um array de 1M elementos?
Q5Você passa o método de uma classe como callback: const f = c.m; f(); — TypeError em strict mode. Por quê?
Destrava
01-07 é prereq dos seguintes módulos: