JavaScript ES6: faiblesses

En juin 2018, la norme ECMAScript 2015 ( ES6 ) a célébré son troisième anniversaire. Dans ES6, d'une part, de nombreuses nouvelles fonctionnalités JavaScript sont apparues, et d'autre part, une nouvelle ère de développement de langage commence avec cette norme. De plus, il s'agissait de la dernière version à grande échelle de JS, car le TC39 applique désormais le schéma de publication de petits numéros annuels de la norme et ne le réédite plus toutes les quelques années.



Les 4 dernières années, ES6, à juste titre, a attiré l'attention universelle. L'auteur du matériel, dont nous publions la traduction aujourd'hui, dit que pendant tout ce temps, grâce à Babel , il a écrit tout le code en utilisant la version moderne des spécifications JS. Il estime que suffisamment de temps s'est écoulé pour analyser de manière critique les nouvelles fonctionnalités d'ES6. En particulier, il s'intéresse à ce qu'il a utilisé pendant un certain temps, puis a cessé de l'utiliser car cela a aggravé son code.

À propos des faiblesses de JS


Douglas Crockford, dans son livre JavaScript: Strengths, a également écrit sur ce qui peut être considéré comme des faiblesses du langage. C'est quelque chose qui, à son avis, ne vaut pas la peine d'être utilisé. Heureusement, parmi les innovations d'ES6, rien n'est aussi disgracieux que certaines des anciennes fonctionnalités problématiques de JS, telles que l'opérateur d'égalité laxiste qui effectue une conversion de type implicite, la fonction eval() et l'instruction with . Les nouvelles fonctionnalités d'ES6 sont bien mieux conçues. Cependant, il y a des choses en lui que j'évite. Les fonctionnalités qui figurent sur ma liste de «faiblesses» JS figurent sur cette liste pour les raisons suivantes:

  • Ce sont essentiellement des «pièges». Autrement dit, il semble qu'ils soient conçus pour effectuer certaines actions et, dans la plupart des cas, ils fonctionnent comme prévu. Cependant, ils se comportent parfois de manière inattendue, ce qui peut facilement entraîner des erreurs.
  • Ils augmentent le volume de la langue en échange de petits avantages. De telles opportunités donnent au développeur quelques petits avantages, mais nécessitent que quelqu'un qui essaie de comprendre son code ait une connaissance de certains mécanismes, généralement cachés quelque part. Cela est doublement vrai pour les capacités de l'API, lorsque l'utilisation de cette fonctionnalité signifie que tout autre code qui interagit avec le code écrit par un certain développeur doit être conscient de l'application de cette fonctionnalité de l'API.

Maintenant, guidés par ces considérations, parlons des faiblesses d'ES6.

Mot-clé const


Avant ES6, les variables en JavaScript pouvaient être déclarées à l'aide du mot clé var . De plus, les variables n'ont pas pu être déclarées du tout, alors elles, même si elles sont utilisées dans des fonctions, tombent dans la portée globale. Les propriétés des objets peuvent jouer le rôle de variables et les fonctions sont déclarées à l'aide du mot clé function . Le mot-clé var a certaines fonctionnalités.

Ainsi, il vous permet de créer des variables qui sont ajoutées à l'objet global, ou celles dont la portée est limitée par des fonctions. Cependant, le mot clé var ne fait pas attention aux blocs de code. De plus, vous pouvez faire référence à une variable déclarée à l'aide du mot-clé var dans le code situé avant la commande pour sa déclaration. Ce phénomène est appelé augmentation des variables. Ces fonctionnalités, si elles ne sont pas prises en compte, peuvent entraîner des erreurs. Afin de rectifier la situation, ES6 a introduit deux nouveaux mots clés pour déclarer des variables: let et const . Ils ont résolu les principaux problèmes var . À savoir, nous parlons du fait que les variables déclarées à l'aide de ces mots clés ont une portée de bloc, par conséquent, par exemple, une variable déclarée dans une boucle n'est pas visible à l'extérieur. De plus, l'utilisation de let et const ne permet pas d'accéder aux variables avant qu'elles ne soient déclarées. Cela entraînera une erreur ReferenceError . Ce fut un grand pas en avant. Cependant, l'émergence de deux nouveaux mots clés, ainsi que leurs fonctionnalités, a conduit à une confusion supplémentaire.

La valeur d'une variable (constante) déclarée à l'aide du mot clé const ne peut pas être remplacée après la déclaration. C'est la seule différence entre const et let . Cette nouvelle opportunité semble utile, et elle peut vraiment apporter des avantages. Le problème est le mot clé const lui-même. Le comportement des constantes déclarées qui l'utilisent ne correspond pas à ce que la plupart des développeurs associent au concept de «constante».

 const CONSTANT = 123; //      "TypeError: invalid assignment to const `CONSTANT`" CONSTANT = 345; const CONSTANT_ARR = [] CONSTANT_ARR.push(1) //     [1]  -    console.log(CONSTANT_ARR) 

L'utilisation du mot clé const empêche l'écriture d'une nouvelle valeur dans une constante, mais ne rend pas immuable les objets référencés par de telles constantes. Cette fonctionnalité offre une faible protection contre les modifications de valeurs lors de l'utilisation de la plupart des types de données. Par conséquent, étant donné que l'utilisation de const peut entraîner de la confusion et du fait que si le mot clé let est présent, la présence de const semble redondante, j'ai décidé de toujours utiliser let .

Chaînes de modèle marquées


Le mot clé const est un exemple de la façon dont une spécification crée trop de façons de résoudre trop peu de problèmes. Dans le cas de chaînes de modèle balisées, nous avons la situation inverse. La syntaxe de ces chaînes a été considérée par le comité TC39 comme un moyen de résoudre l'interpolation de chaînes et les chaînes multilignes. Ils ont ensuite décidé d'étendre cette opportunité grâce à l'utilisation de macros.

Si vous n'avez jamais vu de chaînes de motifs marquées auparavant, gardez à l'esprit qu'elles sont un peu comme des décorateurs de chaînes. Voici un exemple de collaboration avec MDN :

 var person = 'Mike'; var age = 28; function myTag(strings, personExp, ageExp) { var str0 = strings[0]; // "that " var str1 = strings[1]; // " is a " //  (  ) //     , //  ,      . // var str2 = strings[2]; var ageStr; if (ageExp > 99){   ageStr = 'centenarian'; } else {   ageStr = 'youngster'; } return str0 + personExp + str1 + ageStr; } var output = myTag`that ${ person } is a ${ age }`; console.log(output); // that Mike is a youngster 

Les chaînes de modèle marquées ne peuvent pas être appelées complètement inutiles. Voici un aperçu de certaines de leurs utilisations. Par exemple, ils sont utiles pour effacer le code HTML. Et, pour le moment, leur application démontre l'approche la plus précise dans les situations où vous devez effectuer la même opération sur toutes les données d'entrée d'un modèle de chaîne arbitraire. Cependant, ceci est relativement rare, vous pouvez faire de même en utilisant l'API appropriée (bien qu'une telle solution soit plus longue). Et, pour résoudre la plupart des problèmes, l'utilisation de l'API ne sera pas pire que l'utilisation de chaînes de modèle balisées. Cette fonctionnalité n'ajoute pas de nouvelles fonctionnalités à la langue. Elle ajoute de nouvelles approches pour travailler avec des données qui devraient être familières à ceux qui doivent lire du code écrit à l'aide de chaînes de modèle balisées. Et je m'efforce de faire en sorte que mon code reste aussi propre et compréhensible que possible.

Expressions d'attribution destructives repensées


Certaines fonctionnalités du langage ont fière allure lorsqu'elles sont utilisées pour résoudre des tâches simples, cependant, lorsque les tâches deviennent plus complexes, ces fonctionnalités peuvent devenir incontrôlables. Par exemple, j'aime l'opérateur conditionnel ternaire:

 let conferenceCost = isStudent ? 50 : 200 

Cependant, le code écrit avec son aide, il devient difficile de comprendre si, en utilisant cet opérateur, vous commencez à utiliser des constructions imbriquées:

 let conferenceCost = isStudent ? hasDiscountCode ? 25 : 50 : hasDiscountCode ? 100 : 200; 

On peut en dire autant de l'affectation destructrice. Ce mécanisme vous permet d'extraire les valeurs des variables des objets ou des tableaux:

 let {a} = {a: 2, b: 3}; let [b] = [4, 5]; console.log(a, b) // 2, 4 

De plus, lorsque vous l'utilisez, vous pouvez renommer des variables, obtenir des valeurs imbriquées, définir des valeurs par défaut:

 let {a: val1} = {a: 2, b: 3}; let [{b}] = [{a:3, b:4} , {c: 5, d: 6}]; let {c=6} = {a: 2, c: 5}; let {d=6} = {a: 2, c: 5}; console.log(val1, b, c, d) // 2, 4, 5, 6 

Tout cela est merveilleux - jusqu'à ce qu'il s'agisse de créer des expressions complexes en utilisant toutes ces fonctionnalités. Par exemple, dans l'expression ci-dessous, 4 variables sont déclarées: userName , eventType , eventDate et eventId . Leurs valeurs sont prises à différents endroits de la structure de l'objet eventRecord .

 let eventRecord = { user: { name: "Ben M", email: "ben@m.com" }, event: "logged in", metadata: { date: "10-10-2017" }, id: "123" }; let { user: { name: userName = "Unknown" }, event: eventType = "Unknown Event", metadata: [date: eventDate], id: eventId } = obj; 

Comprendre un tel code est presque impossible. Ce problème peut être résolu en utilisant du code beaucoup plus lisible, si vous utilisez plusieurs opérations de déstructuration ou si vous les abandonnez complètement.

 let eventRecord = { user: { name: "Ben M", email: "ben@m.com" }, event: "logged in", metadata: { date: "10-10-2017" }, id: "123" }; let userName = eventRecord.user.userName || 'Unknown'; let eventDate = eventRecord.metadata.date; let {event:eventType='UnknownEvent', id:eventId} = eventRecord; 

Je n'ai pas de ligne directrice claire indiquant que l'expression de la mission destructrice doit être retravaillée. Cependant, chaque fois que je regarde une expression similaire et que je ne peux pas comprendre instantanément quel problème elle résout, quelles variables y sont utilisées, je comprends qu'il est temps de simplifier le code afin d'améliorer sa lisibilité.

Exportation par défaut


ES6 a une fonctionnalité intéressante. Elle consiste dans la façon dont ses développeurs ont abordé la standardisation de ce qui était fait auparavant avec l'aide de diverses bibliothèques, souvent en concurrence les unes avec les autres. Ainsi, dans la spécification sont apparues des classes, des promesses, des modules. C'est tout ce que la communauté des développeurs JS utilisait avant ES6, le trouvant dans des bibliothèques tierces. Par exemple, les modules ES6 sont un excellent substitut à ce qui s'est répandu dans la guerre des formats AMD / CommonJS et fournissent une syntaxe pratique pour organiser les importations.

Les modules ES6 prennent en charge deux manières principales d'exporter des valeurs: l'exportation nommée et l'exportation par défaut, ou l'exportation par défaut:

 const mainValue = 'This is the default export export default mainValue export const secondaryValue = 'This is a secondary value; export const secondaryValue2 = 'This is another secondary value; 

Un module peut utiliser plusieurs commandes d'exportation nommées, mais une seule commande d'exportation par défaut. Lors de l'importation de ce qui a été exporté à l'aide de la commande d'exportation par défaut, dans le fichier d'importation, vous pouvez donner à tout ce qui est exporté par défaut n'importe quel nom, car aucun nom n'est recherché pendant cette opération. Lorsque vous utilisez l'exportation nommée, vous devez utiliser les noms de variable des fichiers d'exportation, bien que le changement de nom soit également possible.

 //   import renamedMainValue from './the-above-example'; //   import {secondaryValue} from './the-above-example'; //     import {secondaryValue as otherValue} from './the-above-example'; 

L'exportation par défaut a bénéficié d' une attention particulière des développeurs de la norme ES6, et ils ont intentionnellement créé une syntaxe plus simple pour cela. Cependant, dans la pratique, j'ai pu découvrir que l'utilisation de la technologie d'exportation nommée est préférable pour les raisons suivantes.

  1. Lorsque vous utilisez l'exportation nommée, les noms des variables exportées correspondent, par défaut, aux noms des variables importées, ce qui simplifie leur recherche pour ceux qui n'utilisent pas d'outils de développement intelligents.
  2. Lors de l'utilisation d'exportations nommées, les programmeurs utilisant des outils de développement intelligents bénéficient de fonctionnalités pratiques telles que l'importation automatique .
  3. Les exportations nommées vous permettent d'exporter uniformément à partir des modules tout ce que vous voulez, dans les bonnes quantités. L'exportation par défaut limite le développeur à l'exportation d'une seule valeur. Pour contourner ce problème, vous pouvez appliquer l'exportation d'un objet avec plusieurs propriétés. Cependant, cette approche perd la valeur de l'algorithme de tremblement d'arbre utilisé pour réduire la taille des applications JS construites par quelque chose comme webpack. L'utilisation de modules nommés exclusivement nommés simplifie le travail.

En général, on peut noter que la dénomination des entités est une bonne pratique, car elle vous permet de les identifier de manière unique à la fois dans le code et dans les conversations sur ce code. C'est pourquoi j'utilise l'export nommé.

Résumé


Vous venez d'apprendre les fonctionnalités d'ES6 qui, selon l'auteur de ce document, n'ont pas réussi. Peut-être que vous rejoignez cette opinion, peut-être pas. Tout langage de programmation est un système complexe, dont les capacités peuvent être vues de différents points de vue. Cependant, nous espérons que cet article sera utile à tous ceux qui cherchent à écrire du code clair et de haute qualité.

Chers lecteurs! Y a-t-il quelque chose dans le JavaScript moderne que vous essayez d'éviter?

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


All Articles