Desafio JavaScript
Contador com contexto preservado
Enunciado
Crie uma classe Contador que possui um valor interno e um método incrementar que aumenta o valor em 1 e imprime o novo valor. O método incrementar deve funcionar mesmo quando extraído da instância (por exemplo, const inc = contador.incrementar; inc();). Implemente duas versões: uma usando bind no construtor e outra usando arrow function como método de classe. Teste ambas.
Requisitos
- A classe
Contadordeve ter um valor inicial 0. - O método
incrementardeve incrementar e imprimir o valor. - A versão com
binddeve fixar othisno construtor. - A versão com arrow function deve ser um método de classe definido como arrow function.
- Ambas devem funcionar quando o método é extraído.
Código inicial
// Versão 1: bind no construtor
class ContadorBind {
constructor() {
this.valor = 0;
// complete
}
incrementar() {
// complete
}
}
// Versão 2: arrow function
class ContadorArrow {
constructor() {
this.valor = 0;
}
// complete com arrow function
}
// Teste
const c1 = new ContadorBind();
const inc1 = c1.incrementar;
inc1();
inc1();
const c2 = new ContadorArrow();
const inc2 = c2.incrementar;
inc2();
inc2();
Saída esperada
1
2
1
2
Ver dica
Na versão com bind, use this.incrementar = this.incrementar.bind(this) no construtor. Na versão com arrow, defina incrementar = () => { ... } como propriedade de instância.
Mostrar solução
// Versão 1: bind no construtor
class ContadorBind {
constructor() {
this.valor = 0;
// Fixa o this do método incrementar à instância
this.incrementar = this.incrementar.bind(this);
}
incrementar() {
this.valor++;
console.log(this.valor);
}
}
// Versão 2: arrow function como método
class ContadorArrow {
constructor() {
this.valor = 0;
}
// Arrow function captura o this da instância
incrementar = () => {
this.valor++;
console.log(this.valor);
};
}
// Teste da versão com bind
const c1 = new ContadorBind();
const inc1 = c1.incrementar; // extrai o método
inc1(); // 1
inc1(); // 2
// Teste da versão com arrow
const c2 = new ContadorArrow();
const inc2 = c2.incrementar; // extrai o método
inc2(); // 1
inc2(); // 2
Passo a passo
- Na classe
ContadorBind, o construtor inicializathis.valor = 0. - Ainda no construtor,
this.incrementar = this.incrementar.bind(this)cria uma nova função ligada à instância e a atribui como propriedade própria, sobrescrevendo o método do protótipo. - O método
incrementarincrementathis.valore imprime. - Na classe
ContadorArrow, o métodoincrementaré definido como uma arrow function no corpo da classe, o que o torna uma propriedade de instância (não do protótipo) e captura othisléxico da instância. - Criamos instâncias e extraímos seus métodos para variáveis (
inc1,inc2). - Chamamos as funções extraídas; como o
thisestá fixado, ambas incrementam o valor correto e imprimem 1 e 2.
Por que funciona
Na versão com bind, o método original é substituído por uma versão ligada à instância, então mesmo quando chamado sem contexto, this é a instância. Na versão com arrow function, o this é capturado do escopo onde a arrow foi definida, que é o construtor da classe (a instância). Assim, ambos resolvem a perda de contexto que ocorreria com um método normal extraído.
Erros comuns
- Esquecer de reatribuir no construtor:
this.incrementar.bind(this);sem atribuir não faz efeito. Usethis.incrementar = this.incrementar.bind(this);. - Usar arrow function no protótipo:
ContadorArrow.prototype.incrementar = () => { ... }não captura othisda instância, pois o escopo léxico é o módulo. Defina como propriedade de instância no corpo da classe. - Chamar
bindapós extrair:const inc = c.incrementar; inc.bind(c)();funciona, mas não é a solução pedida (bind no construtor). - Confundir
thisem arrow function de classe: Arrow functions em classes não têmthispróprio; se usadas incorretamente, podem referenciar o escopo global.
Outra forma de resolver
Podemos usar bind diretamente ao extrair: const inc = c1.incrementar.bind(c1);. Isso é útil quando não queremos modificar a classe, mas é menos eficiente se o método for extraído várias vezes. A solução com bind no construtor ou arrow function é preferível para garantir que o método sempre funcione.
Saída esperada
1
2
1
2