Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

Arrow function em JavaScript: sintaxe e o this

A arrow function é curta, mas abriu mão de this, arguments, prototype e new. Cada ausência explicada com código executado no Node e o erro que ela provoca.

Rodolfo Mori6 min de leitura

Arrow function é uma expressão de função escrita com a seta =>. Ela reduz a sintaxe de callbacks e funções curtas, mas não é apenas uma abreviação de function: não cria this, arguments ou prototype próprios e não pode ser chamada com new.

Pense em dois tipos de crachá. Uma função tradicional recebe o crachá de quem a chamou; é daí que pode vir seu this. Uma arrow não emite um crachá novo: ela continua usando o this do ambiente em que foi criada. Tecnicamente, esse comportamento se chama this léxico. Ele resolve o contexto de muitos callbacks, mas torna a arrow inadequada como método que depende do objeto à esquerda do ponto.

Essas ausências são parte do desenho da linguagem. Por isso, a escolha entre arrow e function depende do contexto de execução, não apenas da quantidade de caracteres.

Esta lição continua a de function expression contra declaration — arrow é uma expression, e tudo que vale para expression vale para ela: não é içada, precisa existir antes de ser chamada, e é um valor que você guarda.

Da forma longa à forma curta

Quatro escritas da mesma ideia, do mais verboso ao mais enxuto:

js
const comFunction = function (preco) {
  return preco * 1.18;
};
const arrowLonga = (preco) => {
  return preco * 1.18;
};
const arrowCurta = (preco) => preco * 1.18;
const semParametro = () => 'Club Store';
const doisParametros = (preco, quantidade) => preco * quantidade;

console.log(comFunction(100), arrowLonga(100), arrowCurta(100));
console.log(semParametro(), doisParametros(89.9, 3).toFixed(2));
118 118 118 Club Store 269.70

Duas regras da sintaxe: sem parâmetro nenhum, os parênteses vazios são obrigatórios; com dois ou mais, também. Só o caso de um parâmetro permite omitir — e mesmo assim vale manter, porque o dia em que você acrescentar o segundo parâmetro não vai precisar mexer nos parênteses. A maioria dos guias de estilo, incluindo o padrão do Prettier, mantém os parênteses sempre.

Retorno implícito

Sem chaves, o valor da expressão já é o retorno — não se escreve return. É o que deixa cadeias de array legíveis:

js
const precos = [19.9, 49.9, 129.9, 289.9];

console.log(precos.map((preco) => Number((preco * 1.18).toFixed(2))));
console.log(precos.filter((preco) => preco >= 100));
console.log(precos.reduce((soma, preco) => soma + preco, 0).toFixed(2));
console.log(precos.some((preco) => preco > 250), precos.every((preco) => preco > 10));
[ 23.48, 58.88, 153.28, 342.08 ] [ 129.9, 289.9 ] 489.60 true true

No momento em que você abre chaves, o corpo vira um bloco comum e o return volta a ser obrigatório. Esquecer disso produz uma função que devolve undefined sem reclamar — o mesmo silêncio que a lição de parâmetros e retorno documenta.

Devolver objeto exige parênteses

Aqui está o erro de sintaxe característico da arrow. A chave depois da seta é ambígua: o motor precisa decidir se ela abre um corpo de função ou um objeto literal, e ele sempre escolhe corpo de função.

js
const itens = [
  { nome: 'Teclado mecânico', preco: 289.9 },
  { nome: 'Mouse sem fio', preco: 149.9 },
];

const etiquetas = itens.map((item) => { nome: item.nome, preco: item.preco });

console.log(etiquetas);
file:///private/tmp/loja/arrow-objeto-erro.mjs:6 const etiquetas = itens.map((item) => { nome: item.nome, preco: item.preco }); ^

SyntaxError: Unexpected token ‘:’ at compileSourceTextModule (node:internal/modules/esm/utils:318:16) at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:90:18)

Node.js v24.16.0

Repare em duas coisas. O acento circunflexo aponta para o segundo dois pontos, não para a chave: o motor tinha lido nome: item.nome como um rótulo seguido de expressão — sintaxe válida dentro de um bloco — e só engasgou no preco: seguinte. E o rastro não tem nenhuma linha do seu código depois da primeira: é erro de compilação, não de execução. Nada do arquivo rodou.

A correção é envolver o objeto em parênteses:

js
const itens = [
  { nome: 'Teclado mecânico', preco: 289.9 },
  { nome: 'Mouse sem fio', preco: 149.9 },
];

const etiquetas = itens.map((item) => ({
  nome: item.nome,
  comDesconto: Number((item.preco * 0.9).toFixed(2)),
}));

console.log(etiquetas);
[ { nome: 'Teclado mecânico', comDesconto: 260.91 }, { nome: 'Mouse sem fio', comDesconto: 134.91 } ]

A arrow não tem this próprio

Esta é a diferença que importa. Uma function recebe this de quem a chamou; uma arrow herda o this do lugar onde foi escrita, e ninguém consegue mudar isso depois.

No topo de um módulo ES, this é undefined — e é esse undefined que uma arrow escrita ali herda:

js
console.log('this no topo do módulo:', this);

const ondeEstou = () => this;
console.log('this herdado pela arrow:', ondeEstou());
this no topo do módulo: undefined this herdado pela arrow: undefined

A consequência prática aparece dentro de objetos. Como callback, a arrow é exatamente o que você quer; como método, é exatamente o que você não quer:

js
const pedido = {
  cliente: 'Ana Souza',
  itens: [
    { nome: 'Teclado mecânico', preco: 289.9 },
    { nome: 'Mouse sem fio', preco: 149.9 },
  ],

  etiquetasComArrow() {
    return this.itens.map((item) => `${this.cliente} — ${item.nome}`);
  },

  etiquetasComFunction() {
    return this.itens.map(function (item) {
      return `${this.cliente} — ${item.nome}`;
    });
  },
};

console.log(pedido.etiquetasComArrow());
console.log(pedido.etiquetasComFunction());
[ 'Ana Souza — Teclado mecânico', 'Ana Souza — Mouse sem fio' ] file:///private/tmp/loja/arrow-this-callback.mjs:14 return `${this.cliente} — ${item.nome}`; ^

TypeError: Cannot read properties of undefined (reading ‘cliente’) at file:///private/tmp/loja/arrow-this-callback.mjs:14:22 at Array.map (<anonymous>) at Object.etiquetasComFunction (file:///private/tmp/loja/arrow-this-callback.mjs:13:23) at file:///private/tmp/loja/arrow-this-callback.mjs:20:20 at ModuleJob.run (node:internal/modules/esm/module_job:439:25)

Node.js v24.16.0

Os dois métodos são idênticos, tirando a forma do callback interno. O da arrow funcionou porque ela herdou o this do método etiquetasComArrow, que apontava para pedido. O da function quebrou porque map chama o callback sem dono, e em modo estrito isso significa this === undefined.

Antes de 2015 a gente resolvia isso guardando var self = this no começo do método — const também não existia ainda — ou passando this como segundo argumento de map. A arrow tornou os dois desnecessários. Os detalhes das quatro regras que decidem o this de cada chamada estão em this em JavaScript.

A arrow não tem arguments — mas enxerga o de fora

Uma arrow no topo do arquivo que use argumentsReferenceError, porque o nome não existe em lugar nenhum. Dentro de uma function, o efeito é mais sutil: a arrow enxerga o arguments da função que a contém.

js
function aplicarPromocao(percentual) {
  const listar = () => Array.from(arguments).slice(1);
  return { percentual, produtos: listar() };
}

console.log(aplicarPromocao(15, 'Teclado mecânico', 'Mouse sem fio'));
{ percentual: 15, produtos: [ 'Teclado mecânico', 'Mouse sem fio' ] }

Funcionou — e é justamente o tipo de coisa que confunde quem lê depois, porque a arrow parece estar acessando os próprios argumentos e não está. Escreva com rest, que é explícito e funciona igual nas duas sintaxes:

js
const aplicarPromocao = (percentual, ...produtos) => ({ percentual, produtos });

console.log(aplicarPromocao(15, 'Teclado mecânico', 'Mouse sem fio'));
console.log(aplicarPromocao(30));
{ percentual: 15, produtos: [ 'Teclado mecânico', 'Mouse sem fio' ] } { percentual: 30, produtos: [] }

A arrow não pode ser chamada com new

Função construtora precisa de duas coisas que a arrow não tem: um this próprio para o objeto novo e um prototype de onde herdar métodos.

js
const Produto = (nome, preco) => {
  this.nome = nome;
  this.preco = preco;
};

const teclado = new Produto('Teclado mecânico', 289.9);
console.log(teclado);
file:///private/tmp/loja/arrow-new.mjs:6 const teclado = new Produto('Teclado mecânico', 289.9); ^

TypeError: Produto is not a constructor at file:///private/tmp/loja/arrow-new.mjs:6:17 at ModuleJob.run (node:internal/modules/esm/module_job:439:25)

Node.js v24.16.0

A ausência do prototype dá para conferir na mão:

js
function ProdutoNormal(nome) {
  this.nome = nome;
}
const ProdutoArrow = (nome) => ({ nome });

console.log('prototype normal:', typeof ProdutoNormal.prototype);
console.log('prototype arrow: ', typeof ProdutoArrow.prototype);
console.log(Object.getOwnPropertyNames(ProdutoNormal));
console.log(Object.getOwnPropertyNames(ProdutoArrow));
prototype normal: object prototype arrow: undefined [ 'length', 'name', 'prototype' ] [ 'length', 'name' ]

Uma arrow tem duas propriedades; uma function tem três. Essa propriedade a mais é a diferença inteira entre poder e não poder construir objetos. Hoje isso raramente importa, porque construtor virou class — e class também não aceita arrow no lugar de constructor.

Onde usar e onde não usar

lugar arrow? motivo
callback de map, filter, reduce sim curta, e o this herdado é o que você quer
callback dentro de método de objeto sim resolve a perda de contexto sem bind
método de objeto ou de classe não precisa do this de quem chamou
função construtora não não aceita new, não tem prototype
listener de DOM que usa event.currentTarget depende com arrow, this não é o elemento; use o parâmetro do evento
function* (generator) não não existe arrow generator

A regra prática: arrow quando você quer que o this de fora entre; function quando você quer que o this de dentro venha da chamada. Toda vez que a resposta for “não uso this aqui”, as duas servem — e aí escolha a mais curta.

Para verificar, crie um objeto contador com dois métodos: normal() { return this === contador } e arrow: () => this === contador. Chame os dois. O método tradicional deve devolver true; a arrow, false, porque não recebe contador como this. Explique o resultado pelo lugar em que cada função obteve seu contexto.

A próxima lição desta trilha mostra o lugar onde a arrow mais aparece: função callback em JavaScript. Se a mensagem Cannot read properties of undefined já apareceu no seu terminal por causa de this, o guia desse erro cobre as outras três origens dele.

  • arrow function
  • this
  • retorno implicito
  • callback
  • sintaxe

Perguntas frequentes

Posso escrever todo o meu código só com arrow function?
Pode compilar, mas você perde método de objeto com this, função construtora e generator. Na prática o código fica cheio de bind e de objetos passados na mão. Arrow é excelente para callback e para função curta; para método, use a sintaxe curta de método.
Por que a arrow precisa de parênteses para devolver um objeto?
Porque a chave depois da seta é ambígua: o motor lê como início de corpo de função, não como objeto literal. Os parênteses forçam o contexto de expressão. Sem eles o erro é de sintaxe, então o arquivo inteiro nem chega a rodar.
Arrow function é mais rápida que function?
Não de forma que você consiga medir num projeto real. Ela cria menos coisas — não tem prototype nem arguments — mas o motor otimiza os dois casos. A escolha é sobre this e legibilidade, nunca sobre desempenho.
Arrow function pode ser async?
Pode, basta escrever async antes dos parâmetros. O que ela não pode ser é generator: não existe arrow com asterisco. Para function* você precisa da palavra function.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

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

  1. MDN — Arrow function expressions — developer.mozilla.org
  2. MDN — this — developer.mozilla.org
  3. ECMAScript 2026 — Arrow Function Definitions — tc39.es

Continue por aqui