Como executar JavaScript no navegador e no Node
Rodar JavaScript no console, em arquivo com o Node e dentro de uma página HTML — com defer, async, type=module e o erro do script parado no head.
Você pode executar JavaScript no console do navegador, em um arquivo processado
pelo Node ou dentro de uma página que carrega uma tag <script>. A linguagem é
a mesma; o ambiente de execução muda as APIs disponíveis e o momento em que
o código começa.
Pense na mesma receita preparada em três cozinhas. A receita é o JavaScript. O
console é uma bancada de teste; o Node oferece recursos do computador, como
arquivos e processos; o navegador oferece document, janelas e eventos. Uma
receita que pede document.querySelector não funciona na cozinha do Node porque
esse ambiente não possui DOM. O erro não está na sintaxe, mas na ferramenta que
não existe ao redor do código.
Para estudar lógica, um arquivo executado com node arquivo.js mantém o caminho
curto. Para manipular uma página, use <script src="app.js" defer></script> no
<head>: defer baixa o arquivo sem bloquear o HTML e só o executa depois que
a árvore da página foi construída.
Esta é a segunda lição da trilha de JavaScript. A anterior explicou o que é a linguagem e onde ela roda; esta coloca o código para rodar de verdade.
Um arquivo e o comando node
Crie um arquivo pedido.js com qualquer editor de texto:
const itens = ['Teclado mecânico', 'Mouse sem fio'];
console.log(`${itens.length} itens no carrinho`);No terminal, na pasta do arquivo:
node pedido.jsÉ isso. Nenhuma configuração, nenhum projeto, nenhum npm init. O Node lê o
arquivo, executa de cima para baixo e sai. Enquanto o assunto for lógica —
variáveis, condições, laços, funções — esse é o ambiente mais rápido que existe,
porque não tem página nem navegador no caminho.
Na página: onde a tag script entra
Numa página, o JavaScript chega por uma tag <script>. Ela pode ter o código
dentro dela ou apontar para um arquivo com src — e, em projeto de verdade,
sempre aponta para arquivo.
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<title>Club Store</title>
<script src="app.js"></script>
</head>
<body>
<button id="finalizar">Finalizar pedido</button>
</body>
</html>E o app.js que essa página carrega:
const botao = document.getElementById('finalizar');
botao.addEventListener('click', () => {
console.log('pedido enviado');
});Abra no navegador e nada funciona. O console mostra um erro, e a razão é
puramente de ordem: o navegador lê o HTML de cima para baixo, encontrou o
<script> no <head>, parou tudo, baixou e executou o arquivo — e nesse
momento o <button> ainda não existia. getElementById devolveu null, e
null não tem método.
Reproduzido no Node, com um document de mentira que devolve null como o
navegador devolveria, o erro é exatamente o mesmo:
const document = { getElementById: () => null };
const botao = document.getElementById('finalizar');
botao.addEventListener('click', () => console.log('pedido enviado'));TypeError: Cannot read properties of null (reading ‘addEventListener’) at file:///private/tmp/loja/l2-nulo.mjs:5:7 Node.js v24.16.0
Repare na palavra do meio: null, não undefined. Isso é informação. null
quer dizer que a busca aconteceu e não achou nada — elemento inexistente ou
página ainda não montada. undefined quer dizer que a propriedade nem existe. A
diferença entre os dois tem lição própria em
null, undefined e NaN, e o diagnóstico
completo dessa família de erro está em
Cannot read properties of undefined.
defer, async e o script parado no head
A correção é uma palavra:
<script src="app.js" defer></script>Com defer, o navegador baixa o arquivo em paralelo com a leitura do HTML e só
executa quando a página inteira estiver montada. O botão existe, o
getElementById acha, o clique funciona.
| atributo | quando baixa | quando executa | mantém a ordem entre scripts |
|---|---|---|---|
| nenhum | na hora, parando o HTML | imediatamente | sim |
defer |
em paralelo | depois do HTML montado | sim |
async |
em paralelo | assim que terminar de baixar | não |
async parece melhor por ser mais agressivo, e é justamente por isso que ele
quebra: dois scripts com async podem executar em qualquer ordem, e o segundo
pode depender do primeiro. Use async só para script que não conversa com o seu
código — medição de audiência, por exemplo.
A alternativa antiga era colocar o <script> no fim do <body>. Funciona, e
ainda aparece em muito tutorial. defer no <head> faz a mesma coisa começando
o download mais cedo.
type=“module” muda o arquivo inteiro
Declarar type="module" não é só ligar import e export. Ele muda quatro
comportamentos de uma vez: o arquivo passa a rodar em modo estrito, ganha escopo
próprio (nada mais vaza para o global), é adiado como se tivesse defer e passa a
exigir servidor — abrir com file:// não funciona.
<script type="module" src="app.js"></script>Com módulos, o código se divide em arquivos que se enxergam por nome:
export function total(itens) {
return itens.reduce((soma, item) => soma + item.preco, 0);
}import { total } from './carrinho.js';
console.log(total([{ preco: 289.9 }, { preco: 149.9 }]));No Node, o mesmo par de arquivos roda com a extensão .mjs, ou com
"type": "module" no package.json. E sim, aquele 439.79999999999995 é o
ponto flutuante de novo — o assunto volta na lição de
tipos de dados.
Ponto e vírgula: o ASI trabalha por você, quase sempre
JavaScript tem inserção automática de ponto e vírgula (ASI, na sigla em
inglês). O interpretador fecha as instruções sozinho quando a quebra de linha não
deixa dúvida. Por isso este arquivo, sem um único ;, roda:
const frete = 24.9
const subtotal = 289.9
const total = subtotal + frete
console.log(total)O problema são as duas situações em que o ASI decide diferente de você. A
primeira é o return com o valor na linha de baixo:
function calcularTotal(itens) {
return
itens.reduce((soma, item) => soma + item.preco, 0);
}
console.log(calcularTotal([{ preco: 289.9 }]));Nenhum erro. O ASI fechou a instrução logo depois do return, a função devolveu
undefined e a linha seguinte virou código morto. Esse é o pior tipo de defeito:
o programa continua, e o undefined vira conta errada três funções depois.
A segunda é a linha que começa com [ ou (. O interpretador não fecha a
anterior, porque a próxima pode ser continuação dela:
const itens = ['Teclado mecânico', 'Mouse sem fio']
const primeiro = itens[0]
[1, 2].forEach((n) => console.log(n))TypeError: itens[0][(1 , 2)].forEach is not a function at file:///private/tmp/loja/l2-asi-colchete.mjs:4:8 Node.js v24.16.0
Leia o que o erro diz que você escreveu: itens[0][(1, 2)]. As duas linhas
viraram uma. É por isso que quem escreve sem ponto e vírgula precisa começar essas
linhas com ;.
Erros comuns
Três, em ordem de frequência:
Cannot read properties of nullao mexer em elemento da página. É script rodando antes do HTML. Ponhadefer, ou confira oidque você buscou.Cannot use import statement outside a module. O arquivo usaimportmas o ambiente não sabe que ele é módulo. No navegador, faltatype="module"; no Node, renomeie para.mjsou declare"type": "module".- Alterar o arquivo e nada mudar na página. É cache do navegador. Recarregue
com
Cmd+Shift+R(ouCtrl+F5) antes de suspeitar do seu código.
O ambiente mínimo para estudar
Um editor de texto, o Node instalado e o navegador que você já usa. Nada além disso nos primeiros três meses — configurar tema, extensão e atalho é a forma mais confortável de adiar o estudo.
Na prática o fluxo é: escreve, salva, node arquivo.js, lê a saída. A próxima
lição é sobre a ferramenta que produz essa saída e que você vai usar mais do que
qualquer outra: console.log — incluindo o que ele
esconde de você quando o objeto é grande. O
guia de JavaScript mostra a ordem completa da trilha.
Para fixar a diferença de ambiente, rode console.log(typeof document) no Node
e no console do navegador. O resultado esperado é undefined no Node e
object no navegador. Depois carregue um arquivo com defer e registre o texto
de um <h1>; se ele aparecer no console sem null, você confirmou também a
ordem correta de execução.
Perguntas frequentes
Devo usar defer ou async no script?
Preciso colocar ponto e vírgula no fim das linhas?
Qual a diferença entre .js e .mjs?
Dá para estudar JavaScript só no console do navegador?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — O elemento script — developer.mozilla.org
- Node.js — Modules: ECMAScript modules — nodejs.org


