Fermer

octobre 5, 2018

Apprivoiser cela en JavaScript avec opérateur de liaison


À propos de l'auteur

Willian Martins est un ingénieur front-end spécialisé dans la connexion quotidienne des concepteurs et des développeurs. Il travaille actuellement en tant qu'ingénieur front-end chez…
Pour en savoir plus sur Willian

Il est parfois difficile de traiter avec ce en JavaScript. Mais que se passerait-il si, au lieu de lutter contre cela, nous pouvions nous en servir pour réaliser des choses intéressantes comme la composition de fonctions avec des méthodes virtuelles? C’est ce que nous allons explorer dans cet article sur l’une des fonctionnalités JavaScript potentielles à venir: l’opérateur de liaison.

Voulez-vous découvrir les prochaines fonctionnalités JavaScript passionnantes dont vous n’aviez même pas besoin? Dans cet article, je présenterai l'une de ces propositions qui, si elles étaient acceptées, pourraient changer la façon dont vous écrivez le code de la même manière que l'opérateur de diffusion l'a fait.

Cependant, voici un petit disclaimer: This feature est en cours de développement et de discussion . L’objectif ici est d’ajouter du battage médiatique et de sensibiliser le public au travail acharné accompli par TC39 pour trouver un consensus, résoudre tous les problèmes de syntaxe et de sémantique et l’avoir fourni avec les prochaines versions d’ECMAScript. Si vous avez des préoccupations, des commentaires ou si vous souhaitez exprimer votre soutien, accédez au référentiel de propositions du TC39 ajoutez une étoile à cette fonctionnalité pour montrer votre soutien, ouvrez un problème pour exprimer vos préoccupations et impliquez-vous.

Mais auparavant, je voudrais poser une question simple (mais délicate):

Qu'est-ce que est ceci ?

Dans ECMAScript, ceci a une autre sémantique que cette dans de nombreux autres langages de programmation, où cette fait souvent référence à la portée lexicale. En général, cela se comporte différemment dans la portée globale, au sein d'une fonction, en mode non strict et en mode strict. Décrivons ce comportement en petits exemples.

ceci Dans la portée mondiale

Quelle est la valeur de cette console dans cet exemple?

. 

Au niveau mondial, ce fait référence à l'objet global, tel que la fenêtre du navigateur, de même sur des travailleurs Web et l'objet module.exports dans NodeJS.

this dans l'étendue de la fonction

Au niveau de la fonction, ce comportement dépend de la manière dont la fonction est appelée, et Cet aspect rend difficile de prédire sa valeur. Nous pouvons mieux la comprendre en vérifiant les exemples suivants:

Quelle est la valeur de cette ici?

 function foo () {
  retournez ceci;
}

console.info (ceci);

À l'intérieur d'une fonction, ce comportement commence à avoir un comportement intéressant, car sa valeur dépend de la manière dont la fonction est appelée. Dans l'exemple ci-dessus, ce fait encore référence à la portée globale, à une différence près. Dans NodeJs, cela pointera vers l'objet global au lieu de module.exports .

Définition d'une valeur en cette :

 function foo () {
  this.bar = 'baz';
  retournez ceci;
}

console.info (foo ());
console.info (new foo ());

Si vous définissez une valeur dans cela la place dans le contexte actuel. L'exemple ci-dessus enregistre la portée globale avec la propriété bar avec la valeur baz dans le premier console.info mais elle n'enregistre que {bar: 'baz'} dans la deuxième console.info . Cela arrive parce que l'opérateur new entre autres, limite la valeur de this à l'objet nouvellement créé.

Ce mot clé en mode strict

En mode strict, le [ cette variable ne porte pas implicitement la valeur du contexte. Cela signifie que si son contexte n'est pas défini, sa valeur est définie par défaut sur undefined comme indiqué dans l'extrait suivant. 19659015] function foo () {
  "use strict";
  retournez ceci;
}

console.info (foo ()); //indéfini

Pour définir le contexte de cette en mode strict, vous pouvez définir la fonction en tant que membre d'un objet. Utilisez l'opérateur new Function.prototype.call () Function.prototype.apply () ou Function.prototype.bind () méthodes, par exemple.

 function foo () {
  "use strict";
  retournez ceci;
}

var a = {toto};

foo (); // indéfini
a.foo (); // {foo: ƒunction}
new foo (); // Objet foo {}
foo.call (this); // Fenêtre / Objet global
foo.apply (this); // Fenêtre / Objet global
foo.bind (this) (); // Fenêtre / Objet global

Making this Variable Predictable

À ce stade, vous pouvez vous rendre compte que la valeur de de dans ECMAScript est assez délicate à prévoir. Pour démontrer les techniques disponibles permettant de le rendre prévisible, je voudrais présenter l’exemple suivant qui imite un cas d’utilisation courant de ce .

   

Dans l'exemple ci-dessus, j'ai créé un composant Meowct qui n'a qu'une propriété qui pointe vers l'élément bouton et une méthode appelée meow qui devrait affiche la propriété d'instance de patte dans la console.

La difficulté réside dans le fait que la méthode meow n'est exécutée que lorsque l'utilisateur clique sur le bouton, raison pour laquelle cette balise de bouton a pour contexte, et depuis la balise button n'a aucune propriété de patte, elle enregistre la valeur undefined dans la console.

Pour résoudre ce problème spécifique, nous pouvons utiliser la méthode Function.prototype.bind () pour la lier explicitement à l'instance cat, comme dans l'exemple suivant: 19659015]

La méthode .bind () renvoie une nouvelle fonction liée en permanence au premier paramètre donné, à savoir le contexte. Maintenant, parce que nous avons lié la méthode cat.meow à l'exemple cat this.paw dans la méthode meow pointe correctement vers l'élément de bouton .

Comme alternative à la méthode Function.prototype.bind () nous pouvons utiliser la fonction flèche pour obtenir le même résultat. Il conserve la valeur du lexique ce du contexte environnant et dispense de la nécessité de le lier explicitement, comme dans l'exemple suivant:

   

Bien que les fonctions de flèche résolvent la plupart des cas d'utilisation dans lesquels nous devons explicitement lier explicitement le ce nous avons encore deux cas d'utilisation pour lesquels l'utilisation de la liaison explicite est nécessaire.

Fonction connue utilisant ceci pour fournir un contexte:

 let hasOwnProp = Object.prototype.hasOwnProperty;
let obj = Object.create (null);

obj.hasOwnProperty ('x') // Erreur de type ...

hasOwnProp.call (obj, "x"); //faux

obj.x = 100;

hasOwnProp.call (obj, "x"); // vrai

Supposons pour une raison quelconque que nous avons cet objet obj qui ne s'étend pas Object.prototype mais nous devons vérifier si obj a un ] x propriété en utilisant la méthode hasOwnProperty de Object.prototype . Pour y parvenir, nous devons utiliser la méthode call et explicitement passer obj comme premier paramètre pour le faire fonctionner comme prévu, ce qui semble ne pas être aussi idiomatique.

Extracting A Method

The deuxième cas peut être repéré lorsque nous avons besoin d'extraire une méthode d'un objet comme dans notre exemple MeowctComponent :

   

Ces cas d'utilisation constituent le problème de base que l'opérateur de liaison tente de résoudre.

L'opérateur de liaison ::

L'opérateur de liaison (19459025) consiste en une introduction d'un nouvel opérateur :: (double-colon), qui fait office de sucre de syntaxe pour les deux cas d'utilisation précédents. Il se présente sous deux formats: binaire et unaire .

Sous sa forme binaire, l'opérateur de reliure crée une fonction dont le côté gauche est lié à de . du côté droit, comme dans l'exemple suivant:

 let hasOwnProp = Object.prototype.hasOwnProperty;
let obj = Object.create (null);

obj.hasOwnProperty ('x') // Erreur de type ...

obj :: hasOwnProp ("x"); //faux

obj.x = 100;

obj :: hasOwnProp ("x"); // vrai

Cela semble plus naturel, n'est-ce pas?

Sous sa forme unaire, l'opérateur crée une fonction liée à la base de la référence fournie en tant que valeur pour cette variable comme dans le exemple suivant:

 ...
cat.paw.addEventListener ('clic', :: cat.meow);
// quels desugars à
cat.paw.addEventListener ('clic', cat.meow.bind (cat));
...

Ce qui est génial avec l’opérateur bind, c’est le fait qu’il ouvre de nouvelles possibilités de création de méthodes virtuelles, comme dans cet exemple de lib pour iterable.

 import {map, takeWhile, forEach} de "iterlib";

getPlayers ()
  :: map (x => x.character ())
  :: takeWhile (x => x.strength> 100)
  :: forEach (x => console.log (x));

C’est très utile car le développeur n’a pas besoin de télécharger toute la bibliothèque pour faire de petites choses, ce qui réduit la quantité de JavaScript importé. En outre, cela facilite l'extension de ce type de bibliothèque.

Comment développer à l'aide de Bind Operator

Pour que l'exemple reste simple, supposons qu'il soit nécessaire de créer un module mathématique permettant au développeur de chaîner les opérations pour former un calculateur. expression qui, à partir d’un nombre en entrée, peut effectuer tous les calculs dans un pipeline. Le code pour y parvenir est simple et pourrait être écrit comme suit:

 function plus (x) {
  retourne ce + x;
}

fonction moins (x) {
  retourne ceci - x;
}

temps de fonction (x) {
  renvoyer cette * x;
}

fonction div (x) {
  renvoyer ceci / x;
}

Comme vous pouvez le constater dans l'exemple ci-dessus, nous nous attendons à ce que la valeur soit utilisée comme contexte et nous l'utilisons pour effectuer le calcul. Ainsi, à l'aide de l'opérateur bind, nous pourrions créer une expression comme celle-ci:

 1 :: plus (2) :: times (4) :: div (3) :: moins (1); // renvoie 3

Ce qui équivaut à:

 minus.call (div.call (times.call (plus.call (1, 2), 4), 3), 1);

Le premier extrait semble plus idiomatique, n'est-ce pas?

Pour aller un peu plus loin, nous pouvons l'utiliser pour convertir une température de Celsius en Fahrenheit, ceci peut être accompli par l'expression de fonction suivante:

 const toFahrenheit = x => x :: times (9) :: div (5) :: plus (32);
console.info (toFahrenheit (20)); // 68

Jusqu'à présent, nous montrons comment créer des fonctions pour interagir avec les valeurs, mais qu'en est-il de l'extension de l'objet avec des méthodes virtuelles? Nous pouvons créer de nouvelles compositions de flux en mélangeant des méthodes intégrées à des méthodes personnalisées. Pour le démontrer, nous pouvons composer des méthodes string avec des méthodes personnalisées. Commençons par vérifier le module avec les méthodes personnalisées et son implémentation.

 function capitalize () {
  return this.replace (/ (?: ^ |  s)  S / g, a => a.toUpperCase ());
}

fonction doubleSay () {
  retour `$ {this} $ {this}`;
}

fonction exclamation () {
  retourne '$ {this}! `;
}

Avec ce module en place, nous pouvons réaliser des tâches intéressantes comme suit:

 const {trim, padEnd} = String.prototype;

console.info (
  '   Bonjour le monde   '
    ::réduire()
    ::capitaliser()
    :: doubleSay ()
    ::exclamation()
    :: padEnd (30)
)

// "Bonjour le monde Bonjour le monde!"

Dans l'exemple ci-dessus, vous remarquerez que j'ai extrait deux méthodes des prototypes String. trim () et padEnd () . Comme ces méthodes sont extraites, je peux les utiliser pour composer mon flot de méthodes parallèlement à mes méthodes virtuelles capitalize () doubleSay () et exclamation () . C'est cet aspect qui rend l'opérateur de liaison si passionnant et prometteur.

Avantages et inconvénients de Bind Operator

Comme vous pouvez vous en rendre compte à ce stade, il y a certains aspects que Bind Operator brille. Ceux-ci sont les suivants:

  • Il couvre les deux seuls cas d'utilisation manquants pour lesquels une liaison explicite est nécessaire;
  • Il est facile de rendre cette variable prévisible;
  • il ajoute une nouvelle méthode. étendre la fonctionnalité à l'aide de méthodes virtuelles
  • Il est utile d'étendre les objets intégrés sans étendre la chaîne de prototypes. Vous souvenez-vous de Smoosh Gate ?

De l’autre côté, pour composer des fonctions avec un opérateur de liaison, vous devez vous y fier, ce qui peut entraîner des problèmes, comme dans cet exemple:

 const plus = (x) => this + x;

console.info (1 :: plus (1));
// "[object Window] 1"

Comme cela apparaît clairement dans l’exemple ci-dessus, il n’est pas possible de composer une fonction de flèche avec un opérateur de liaison, car il n’est pas possible de lier cette à une fonction de flèche. Parfois, les utilisateurs ne veulent pas que ce soit obligé de composer leur comportement via une chaîne de fonctions, ce qui peut poser problème si vous utilisez uniquement un opérateur de liaison pour y parvenir.

On dit souvent que l’opérateur de liaison peut entraîner une surcharge de syntaxe, ce qui peut poser problème aux nouveaux venus dans le langage. Se rendre compte qu'un opérateur spécifique fonctionne sous une forme binaire et unaire est également délicat. Une solution possible consiste à introduire la forme binaire dans le langage séparément de la forme unaire. Ainsi, une fois que la forme binaire est intégrée à la langue, le comité peut réévaluer si la forme unaire est encore nécessaire. Pendant ce temps, les utilisateurs peuvent s'habituer à la forme binaire et la surcharge de syntaxe pourrait potentiellement être atténuée.

Conclusion

Prédire la valeur de cette en JavaScript est une astuce. Le langage a quelques règles pour expliquer comment le contexte est assigné à cela, mais au quotidien nous voulons rendre cette valeur prévisible. La méthode Function.prototype.bind () et les fonctions de flèches nous aident à rendre la valeur de prévisible. L'opérateur de liaison vient couvrir les deux cas d'utilisation pour lesquels nous devons encore explicitement lier ce .

L'avènement de l'opérateur de liaison ouvre une opportunité de créer un nouvel ensemble de composition de fonction via des méthodes virtuelles, mais cela peut ajouter une surcharge de syntaxe rendant difficile l'intégration des nouveaux venus dans la langue.

L'auteur de l'opérateur de liaison est Kevin Smith, et cette proposition est au stade 0 . Le TC39 est ouvert aux commentaires. Si vous aimez cette fonctionnalité et pensez que cela est utile, veuillez ajouter une étoile dans le référentiel, si vous avez une idée pour résoudre les problèmes présentés ici, si vous avez un autre moyen de définir la syntaxe ou la sémantique de ces fonctionnalités ou si vous en repérez une autre. problème avec celui-ci, veuillez ouvrir un numéro dans le référentiel et faire part de vos réflexions / idées au comité.

 Éditorial éclatant (dm, ra, yk, il)




Source link