Que faire lorsque «ceci» perd le lien de contexte

Bonjour, Habr! Je vous présente la traduction de l'article «Que faire quand« cela »perd le contexte» de Cristi Salcescu .

La meilleure façon d'éviter de perdre ce contexte est de ne pas l'utiliser. Mais ce n'est pas toujours possible. Par exemple, nous travaillons avec le code ou la bibliothèque d'une autre personne qui l'utilise.

Littéral d'objet, fonction constructeur, constructeur d'objet de classe dans le système prototype. Ce pseudo- paramètre est utilisé dans le système de prototypage afin de donner accès aux propriétés de l'objet.

Regardons quelques cas.

Fonctions imbriquées


cela perd la référence de contexte à l'intérieur des fonctions imbriquées.

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(); 

La méthode doSomething () a deux fonctions imbriquées: doAnotherthing () et log () . Lorsque vous appelez service.doSomething () , cela perd la référence de contexte dans la fonction imbriquée.

bind ()


Une façon de résoudre le problème est d' utiliser la méthode bind () . Jetez un œil au code suivant:

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

bind () crée une nouvelle version de la fonction qui, lorsqu'elle est appelée, a déjà une certaine valeur.

La fonction doAnotherThing () {/*...*/}.bind(this) crée une version de la fonction doAnotherThing () , qui prend la valeur de ceci de doSomething () .

ça / moi


Une autre option consiste à déclarer et à utiliser la nouvelle variable that / self , qui stockera la valeur this de la méthode doSomething () .

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

Nous devons déclarer let that = this dans toutes les méthodes utilisant this dans des fonctions imbriquées.

Fonctions fléchées


La fonction flèche nous donne une autre façon de résoudre ce problème.

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

Les fonctions fléchées ne créent pas leur propre contexte pour cela , mais utilisent plutôt la valeur this du contexte environnant. Dans l'exemple ci-dessus, il utilise la valeur de this de la fonction parent.

L'inconvénient de cette méthode est que nous ne pouvons pas spécifier le nom de la fonction flèche. Le nom de la fonction joue un rôle important, car il augmente la lisibilité du code et décrit son objectif.

Voici le même code avec une fonction exprimée en termes de nom de variable:

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

Fonctions de rappel (méthode comme rappel)


cela perd la référence de contexte lors de l'utilisation de la méthode comme fonction de rappel. Regardons la classe suivante:

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

Examinons les situations dans lesquelles la méthode service.doSomething () est utilisée comme fonction de rappel.

 //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(); } 

Dans tous les cas ci-dessus, cela perd le lien de contexte.

bind ()


Nous pouvons utiliser bind () pour résoudre ce problème. Voici le code de cette option:

 //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)); 

Fonction flèche


Une autre façon consiste à créer une fonction de flèche qui appelle service.doSomething () .

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

Composants React


Dans les composants, cela perd la référence de contexte lorsque les méthodes sont utilisées comme rappels pour les événements.

 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')); 

Comme solution, nous pouvons créer de nouvelles fonctions dans le constructeur qui utiliseront bind (this) .

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

N'utilisez pas «this»


Non, pas de problème de perte de contexte. Les objets peuvent être créés à l'aide des fonctions d'usine . Jetez un œil à cet exemple:

 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 }); } 

Le contexte reste si vous utilisez la méthode comme rappel.

 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); 

Conclusion


cela perd la référence de contexte dans diverses situations.
bind () , en utilisant cette variable / self et les fonctions flèches sont des moyens de résoudre les problèmes de contexte.

Les fonctions d'usine permettent de créer des objets sans l'utiliser.

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


All Articles