O que fazer quando "isto" perde o link de contexto

Olá Habr! Apresento a você a tradução do artigo "O que fazer quando" isso "perde o contexto" por Cristi Salcescu .

A melhor maneira de evitar a perda desse contexto é não usá- lo . No entanto, isso nem sempre é possível. Por exemplo, trabalhamos com o código ou a biblioteca de outra pessoa que usa isso .

Objeto literal, função construtora, construtor de objeto de classe no sistema de protótipo. Este pseudo- parâmetro é usado no sistema de prototipagem para dar acesso às propriedades do objeto.

Vejamos alguns casos.

Funções aninhadas


isso perde a referência de contexto dentro de funções aninhadas.

class Service { constructor(){ this.numbers = [1,2,3]; this.token = "token"; } doSomething(){ setTimeout(function doAnotherThing(){ this.numbers.forEach(function log(number){ //Cannot read property 'forEach' of undefined console.log(number); console.log(this.token); }); }, 100); } } let service = new Service(); service.doSomething(); 

O método doSomething () possui duas funções aninhadas: doAnotherthing () e log () . Ao chamar service.doSomething () , isso perde a referência de contexto na função aninhada.

bind ()


Uma maneira de resolver o problema é com o método bind () . Dê uma olhada no seguinte código:

 doSomething(){ setTimeout(function doAnotherThing(){ this.numbers.forEach(function log(number){ console.log(number); console.log(this.token); }.bind(this)); }.bind(this), 100); } 

bind () cria uma nova versão da função, que quando chamada já tem um certo valor disso .

A função doAnotherThing () {/*...//..bind(this) cria uma versão da função doAnotherThing () , que retira o valor disso de doSomething () .

isso / eu


Outra opção é declarar e usar a nova variável that / self , que armazenará esse valor no método doSomething () .

 doSomething(){ let that = this; setTimeout(function doAnotherThing(){ that.numbers.forEach(function log(number){ console.log(number); console.log(that.token); }); }, 100); } 

Devemos declarar let that = this em todos os métodos usando isso em funções aninhadas.

Funções de seta


A função de seta nos fornece outra maneira de resolver esse problema.

 doSomething(){ setTimeout(() => { this.numbers.forEach(number => { console.log(number); console.log(this.token); }); }, 100); } 

As funções de seta não criam seu próprio contexto para isso , mas usam o valor this do contexto circundante. No exemplo acima, ele usa o valor disso da função pai.

A desvantagem desse método é que não podemos especificar o nome da função de seta. O nome da função desempenha um papel importante, pois aumenta a legibilidade do código e descreve sua finalidade.

Abaixo está o mesmo código com uma função expressa em termos de um nome de variável:

 doSomething(){ let log = number => { console.log(number); console.log(this.token); } let doAnotherThing = () => { this.numbers.forEach(log); } setTimeout(doAnotherThing, 100); } 

Funções de retorno de chamada (método como retorno de chamada)


isso perde a referência de contexto ao usar o método como uma função de retorno de chamada. Vejamos a seguinte classe:

 class Service { constructor(){ this.token = "token"; } doSomething(){ console.log(this.token);//undefined } } let service = new Service(); 

Vejamos situações em que o método service.doSomething () é usado como uma função de retorno de chamada.

 //callback on DOM event $("#btn").click(service.doSomething); //callback for timer setTimeout(service.doSomething, 0); //callback for custom function run(service.doSomething); function run(fn){ fn(); } 

Em todos os casos acima, isso perde o link de contexto.

bind ()


Podemos usar o bind () para resolver esse problema. Abaixo está o código para esta opção:

 //callback on DOM event $("#btn").click(service.doSomething.bind(service)); //callback for timer setTimeout(service.doSomething.bind(service), 0); //callback for custom function run(service.doSomething.bind(service)); 

Função de seta


Outra maneira é criar uma função de seta que chama service.doSomething () .

 //callback on DOM event $("#btn").click(() => service.doSomething()); //callback for timer setTimeout(() => service.doSomething(), 0); //callback for custom function run(() => service.doSomething()); 

Reagir componentes


Nos componentes, isso perde a referência de contexto quando os métodos são usados ​​como retornos de chamada para eventos.

 class TodoAddForm extends React.Component { constructor(){ super(); this.todos = []; } componentWillMount() { this.setState({desc: ""}); } add(){ let todo = {desc: this.state.desc}; //Cannot read property 'state' of undefined this.todos.push(todo); } handleChange(event) { //Cannot read property 'setState' of undefined this.setState({desc: event.target.value}); } render() { return <form> <input onChange={this.handleChange} value={this.state.desc} type="text"/> <button onClick={this.add} type="button">Save</button> </form>; } } ReactDOM.render( <TodoAddForm />, document.getElementById('root')); 

Como solução, podemos criar novas funções no construtor que usarão bind (this) .

 constructor(){ super(); this.todos = []; this.handleChange = this.handleChange.bind(this); this.add = this.add.bind(this); } 

Não use "isto"


Sem isso - sem problemas com a perda de contexto. Objetos podem ser criados usando funções de fábrica . Veja este exemplo:

 function Service() { let numbers = [1,2,3]; let token = "token"; function doSomething(){ setTimeout(function doAnotherThing(){ numbers.forEach(function log(number){ console.log(number); console.log(token); }); }, 100); } return Object.freeze({ doSomething }); } 

O contexto permanece se você usar o método como retorno de chamada.

 let service = Service(); service.doSomething(); //callback on DOM event $("#btn").click(service.doSomething); //callback for timer setTimeout(service.doSomething, 0); //callback for custom function run(service.doSomething); 

Conclusão


isso perde a referência de contexto em várias situações.
bind () , usar essa variável / self e as funções de seta são formas de resolver problemas de contexto.

As funções de fábrica tornam possível criar objetos sem usar isso .

Source: https://habr.com/ru/post/pt421959/


All Articles