
call() e apply() executam imediatamente uma função com um this à escolha; o call recebe os argumentos um a um, o apply recebe-os num array. O bind() não executa nada: devolve uma nova função cujo this (e eventualmente os primeiros argumentos) fica fixado para sempre. Usa-se quando um método é passado como callback e perde o seu objeto.
Todas as funções de JavaScript têm três métodos, call(), apply() e bind(), ao serviço da mesma causa: decidir quanto vale this no momento em que a função é executada. Para perceber para que servem, é preciso perceber primeiro o problema que resolvem: em JavaScript, this não depende do sítio onde a função é escrita, mas da forma como é chamada.
O problema: o this muda consoante a chamada
Quatro regras chegam para prever this em quase todos os casos:
- Chamada de método
objet.fonction():thiséobjet. - Chamada simples
fonction():thiséundefinedem modo estrito (módulos, classes) e o objeto globalwindowem modo não estrito. - Chamada com
new:thisé o objeto que está a ser construído. - Chamada através de
call,applyou de uma função ligada porbind:thisé aquilo que tu decidiste.
As arrow functions escapam a estas regras: não têm this próprio e ficam com o do código que as rodeia.
O caso que dói na prática é a passagem da regra 1 para a regra 2:
const person = {
name: 'Sarah',
hello() {
return `Bonjour ${this.name}`;
},
};
person.hello(); // 'Bonjour Sarah' — chamada de método, this = person
const detached = person.hello;
detached(); // TypeError em modo estrito — this vale undefined
// ('Bonjour undefined' ou 'Bonjour ' em modo não estrito)A função não mudou. Mudou apenas a maneira de a chamar, e o this com ela. É exatamente o que acontece quando se passa um método a setTimeout, a addEventListener ou a map.
call(): chamar com um this à escolha
fonction.call(thisArg, arg1, arg2, …) executa a função imediatamente, com this igual a thisArg e os argumentos passados um a um.
function presenter(age, ville) {
return `${this.name}, ${age} ans, ${ville}`;
}
const person = { name: 'Sarah' };
presenter.call(person, 30, 'Lille'); // 'Sarah, 30 ans, Lille'
presenter.call({ name: 'Léa' }, 25, 'Paris'); // 'Léa, 25 ans, Paris'
const detached = person.hello; // o método do exemplo anterior
detached.call(person); // 'Bonjour Sarah' — o this foi repostoUma utilização clássica: reaproveitar um método de array em algo que não é um array.
Array.prototype.slice.call('abc'); // ['a', 'b', 'c']
// Hoje escreve-se antes:
Array.from('abc'); // ['a', 'b', 'c']apply(): o mesmo, com os argumentos num array
fonction.apply(thisArg, [arg1, arg2, …]) faz exatamente o que faz call, mas recebe os argumentos sob a forma de array. Prático quando já chegam agrupados.
presenter.apply(person, [30, 'Lille']); // 'Sarah, 30 ans, Lille'
const notes = [3, 9, 4];
Math.max.apply(null, notes); // 9 — Math.max espera argumentos separados, não um arrayDesde o ES2015, o operador de spread torna o apply raramente necessário: Math.max(...notes) e presenter.call(person, ...args) fazem o mesmo trabalho sem perder legibilidade. O apply aparece sobretudo em código escrito antes disso.
bind(): fixar o this para mais tarde
Onde call e apply executam de imediato, bind não executa nada. Devolve uma nova função cujo this fica fixado de vez. É a ferramenta dos callbacks: prepara-se a função agora, outra pessoa chama-a mais tarde.
const bound = person.hello.bind(person);
bound(); // 'Bonjour Sarah', seja qual for o contexto da chamada
bound.call({ name: 'X' }); // 'Bonjour Sarah' — o bind ganha, o call já não muda o this
bound.name; // 'bound hello'O caso típico: um método passado como callback
class Compteur {
constructor() {
this.n = 0;
}
incrementer() {
this.n += 1;
return this.n;
}
}
const compteur = new Compteur();
// Sem bind — o método está separado do seu objeto
const inc = compteur.incrementer;
inc(); // TypeError: Cannot read properties of undefined (reading 'n')
// Com bind — o this fica fixado em compteur
const incLie = compteur.incrementer.bind(compteur);
incLie(); // 1
incLie(); // 2O mesmo esquema aplica-se aos temporizadores e aos eventos do navegador:
setTimeout(compteur.incrementer.bind(compteur), 1000);
bouton.addEventListener('click', compteur.incrementer.bind(compteur));
// Para poderes remover o listener depois, guarda a referência ligada:
const onClick = compteur.incrementer.bind(compteur);
bouton.addEventListener('click', onClick);
bouton.removeEventListener('click', onClick);Sem bind, o addEventListener chamaria incrementer com this igual ao botão clicado: this.n seria undefined e o contador não contaria nada.
bind() para a aplicação parcial
Os argumentos passados a bind a seguir ao this também ficam fixados. A função devolvida espera os seguintes.
const presenterSarah = presenter.bind(person, 30);
presenterSarah('Lille'); // 'Sarah, 30 ans, Lille' — só falta fornecer o último argumento
const add = (a, b) => a + b;
const add5 = add.bind(null, 5); // o this não serve aqui — basta null
add5(3); // 8É uma forma simples de especializar uma função genérica sem escrever uma função invólucro.
Arrow functions: quando o bind deixa de servir
Uma arrow function capta o this do sítio onde é escrita e ignora tudo o resto. Daí duas consequências.
Primeiro, call, apply e bind não conseguem mudar o seu this:
const arrow = () => this;
arrow.call({ a: 1 }); // o this do módulo, não { a: 1 }Depois, e é este o uso corrente, uma arrow declarada dentro de um método ou de uma classe mantém o this certo sem bind:
class Compteur {
n = 0;
// Propriedade arrow — o this é capturado de uma vez por todas
incrementer = () => {
this.n += 1;
return this.n;
};
}
const c = new Compteur();
const inc = c.incrementer;
inc(); // 1 — funciona sem bind
setTimeout(() => c.incrementer(), 1000); // ou uma arrow na chamadaO reverso da medalha: uma propriedade arrow é recriada em cada instância, ao passo que um método clássico é partilhado pelo protótipo. Para alguns objetos, não tem importância; para milhares de instâncias, o método ligado uma vez no construtor (this.incrementer = this.incrementer.bind(this)) poupa memória.
O this passado como segundo argumento
Vários métodos de array aceitam diretamente o valor de this a usar no callback, o que evita um bind:
const config = { k: 10 };
[1, 2, 3].map(function (x) { return this.k * x; }, config); // [10, 20, 30]forEach, filter, some e every têm o mesmo segundo argumento. Com uma arrow function, deixa de ser preciso.
Tabela comparativa
call | apply | bind | |
|---|---|---|---|
| Executa a função | imediatamente | imediatamente | não: devolve uma função |
| Argumentos | um a um | num array | um a um, fixados para as chamadas futuras |
| Utilização típica | reaproveitar um método | espalhar um array (substituído por ...) | callbacks, eventos, aplicação parcial |
| Efeito numa arrow function | nenhum sobre this | nenhum sobre this | nenhum sobre this, os argumentos ficam fixados |
Erros frequentes e como os ler
- «Cannot read properties of undefined (reading ‘n’)» num método de classe: o método foi separado da sua instância (regra 2). Liga-o com
bindou declara-o como propriedade arrow. - Um listener de evento que não altera o estado:
thisé o elemento do DOM, não o teu objeto. O remédio é o mesmo. - Um
bindque parece não fazer nada: a função é uma arrow. Tira obind, não serve para nada. - Um
removeEventListenerque não retira nada: cadabindcria uma função nova; é preciso passar a mesma referência ligada na adição e na remoção.
Para a seguir, as classes em JavaScript voltam aos métodos e ao protótipo, e o capítulo As funções do tutorial assenta as bases se o vocabulário deste artigo ainda for novo. Em PHP, a questão põe-se de outra maneira: $this nunca muda de objeto.
Erros frequentes
bouton.addEventListener('click', compteur.incrementer) chama a função com this igual ao botão, não a compteur. Escreve compteur.incrementer.bind(compteur) ou uma arrow function.this próprio: call, apply e bind não o alteram. Só os argumentos passados pelo bind contam.bind cria uma função nova em cada chamada. Num ciclo ou numa renderização repetida, liga uma vez no construtor (ou declara o método como propriedade arrow).f.call(obj, [1, 2]) passa um único argumento, o array. Para espalhar um array é o apply, ou f.call(obj, ...tableau).

