Fonctionnalités JavaScript peu connues

JavaScript est souvent appelé le langage le plus simple pour les débutants, dans la programmation qui est la plus difficile à maîtriser. L'auteur du matériel, dont nous publions la traduction, déclare qu'il ne peut qu'être d'accord avec cette déclaration. Le fait est que JS est un langage vraiment ancien et vraiment flexible. Il est plein de constructions syntaxiques mystérieuses et de fonctionnalités obsolètes qu'il prend toujours en charge.

image

Aujourd'hui, nous allons parler des fonctionnalités et des options JavaScript peu connues pour leur application pratique.

JavaScript est toujours quelque chose de nouveau


Je travaille avec JavaScript depuis de nombreuses années et je rencontre constamment quelque chose que je n'aurais jamais soupçonné d'exister. Ici, j'ai essayé d'énumérer des fonctionnalités similaires peu connues du langage. En mode strict, certains d'entre eux ne fonctionneront pas, mais en mode normal, ce sont des exemples de code JS complètement corrects. Il convient de noter que je ne prétends pas conseiller aux lecteurs de mettre tout cela en service. Bien que ce dont je vais vous parler vous semble très intéressant, vous pouvez commencer à utiliser tout cela si vous travaillez en équipe et, pour le moins, surprendre vos collègues.

→ Le code dont nous allons discuter ici se trouve ici.

Veuillez noter que je n'ai pas inclus des choses comme l'augmentation des variables, les fermetures, les objets proxy, l'héritage du prototype, async / wait, les générateurs, etc. Bien que ces caractéristiques du langage puissent être attribuées à des difficultés de compréhension, elles ne sont pas bien connues.

Opérateur nul


JavaScript a un opérateur unary void . Vous pouvez l'avoir rencontré sous la forme de void(0) ou void 0 . Son seul but est de calculer l'expression à sa droite et de la rendre undefined . 0 ici, il est utilisé simplement parce qu'il est habituel, bien que ce ne soit pas nécessaire, et ici vous pouvez utiliser n'importe quelle expression valide. Certes, cet opérateur retournera en tout cas undefined .

 //  void void 0                  // undefined void (0)                // undefined void 'abc'              // undefined void {}                 // undefined void (1 === 1)          // undefined void (1 !== 1)          // undefined void anyfunction()      // undefined 

Pourquoi ajouter un mot-clé spécial à la langue qui sert à renvoyer undefined , si vous pouvez simplement utiliser la valeur standard undefined ? N'est-ce pas, il y a une redondance?

Il s'est avéré qu'avant l'apparition de la norme ES5 dans la plupart des navigateurs, une valeur standard undefined pouvait se voir attribuer une nouvelle valeur. Supposons que vous puissiez exécuter cette commande avec succès: undefined = "abc" . Par conséquent, une valeur undefined peut ne pas être ce qu'elle devrait être. À cette époque, l'utilisation du void nous permettait d'assurer la confiance dans l'utilisation du réel undefined .

Les crochets lors de l'appel des constructeurs sont facultatifs


Les crochets ajoutés après le nom de la classe, en invoquant le constructeur, sont complètement facultatifs (sauf si le constructeur a besoin de passer des arguments).

Dans l'exemple suivant, la présence ou l'absence de parenthèses n'affecte pas le bon fonctionnement du programme.

 //     const date = new Date() const month = new Date().getMonth() const myInstance = new MyClass() //     const date = new Date const month = (new Date).getMonth() const myInstance = new MyClass 

Les supports ne peuvent pas être utilisés avec IIFE


La syntaxe IIFE m'a toujours paru étrange. Pourquoi y a-t-il tous ces supports?

Il s'est avéré que les crochets ne sont nécessaires que pour indiquer à l'analyseur JavaScript qu'un code est une expression fonctionnelle et non une tentative incorrecte de déclarer une fonction. La connaissance de ce fait nous permet de comprendre qu'il existe de nombreuses façons de se débarrasser des crochets dans lesquels IIFE est enfermé, et en même temps d'écrire du code de travail.

 // IIFE (function () { console.log('Normal IIFE called') })() // Normal IIFE called void function () { console.log('Cool IIFE called') }() // Cool IIFE called 

Ici, l'opérateur void indique à l'analyseur que le code qui le suit est une expression fonctionnelle. Cela permet de se débarrasser des crochets autour de la déclaration de fonction. Et d'ailleurs, ici, vous pouvez utiliser n'importe quel opérateur unaire ( void , + , ! , - , etc.) et le code continuera de fonctionner. N'est-ce pas merveilleux?

Cependant, si vous êtes un lecteur attentif, vous vous demandez peut-être que l'opérateur unaire affecte le résultat renvoyé par IIFE. En fait, c'est comme ça. Mais la bonne chose est que si vous avez besoin du résultat de IIFE, que vous, par exemple, stockez dans une variable, alors vous n'avez pas besoin de parenthèses autour de IIFE. Voici un exemple.

 // IIFE,    let result = (function () { // ... -  return 'Victor Sully' })() console.log(result) // Victor Sully let result1 = function () { // ... -  return 'Nathan Drake' }() console.log(result1) // Nathan Drake 

Les accolades autour du premier IIFE améliorent seulement la lisibilité du code sans affecter son fonctionnement.

Si vous voulez mieux comprendre l'IIFE, jetez un œil à ce matériel.

Construction avec


Savez-vous que JavaScript possède une construction with qui prend en charge les blocs d'expression? Cela ressemble à ceci:

 with (object)  statement //       with (object) {  statement  statement  ... } 

La construction with ajoute toutes les propriétés de l'objet qui lui sont passées dans la chaîne de portée utilisée lors de l'exécution des commandes.

 //    with const person = { firstname: 'Nathan', lastname: 'Drake', age: 29 } with (person) { console.log(`${firstname} ${lastname} is ${age} years old`) } // Nathan Drake is 29 years old 

with peut sembler un excellent outil. Il semble que ce soit encore mieux que les nouvelles fonctionnalités de JS pour la déstructuration d'objets , mais en fait ce n'est pas le cas.

La construction with est obsolète et son utilisation n'est pas recommandée. En mode strict, son utilisation est interdite. Il s'avère que les blocs provoquent des problèmes de performances et de sécurité.

Constructeur de fonctions


L'utilisation du mot-clé function n'est pas le seul moyen de définir une nouvelle fonction. Vous pouvez définir des fonctions de manière dynamique à l'aide du constructeur Function et du new opérateur. Voici à quoi ça ressemble.

 //  Function const multiply = new Function('x', 'y', 'return x*y') multiply(2,3) // 6 

Le dernier argument passé au constructeur est une chaîne avec le code de fonction. Deux autres arguments sont des paramètres de fonction.

Il est intéressant de noter que le constructeur Function est le "parent" de tous les constructeurs en JavaScript. Même le constructeur Object est un constructeur Function . Et le constructeur natif de Function est également Function . Par conséquent, un appel du type object.constructor.constructor... effectué pour tout objet JS un nombre suffisant de fois renverra le constructeur Function en conséquence.

Propriétés des fonctionnalités


Nous savons tous que les fonctions en JavaScript sont des objets de première classe. Par conséquent, personne ne nous empêche d'ajouter de nouvelles propriétés aux fonctions. C'est parfaitement normal, mais rarement utilisé.

Quand cela peut-il être nécessaire?

En fait, il existe plusieurs situations dans lesquelles cette fonctionnalité peut être utile. Considérez-les.

▍ Caractéristiques personnalisées


Supposons que nous ayons une fonction greet() . Nous avons besoin qu'elle affiche différents messages de bienvenue en fonction des paramètres régionaux utilisés. Ces paramètres peuvent être stockés dans une variable externe à la fonction. De plus, la fonction peut avoir une propriété qui définit ces paramètres, en particulier les paramètres de langue de l'utilisateur. Nous utiliserons la deuxième approche.

 //  ,   function greet () { if (greet.locale === 'fr') {   console.log('Bonjour!') } else if (greet.locale === 'es') {   console.log('Hola!') } else {   console.log('Hello!') } } greet() // Hello! greet.locale = 'fr' greet() // Bonjour! 

▍Fonctions avec variables statiques


Voici un autre exemple similaire. Supposons que nous ayons besoin d'implémenter un certain générateur qui produit une séquence de nombres ordonnés. Habituellement, dans de telles situations, afin de stocker des informations sur le dernier nombre généré, des variables de compteur statiques dans les classes ou IIFE sont utilisées. Avec cette approche, nous limitons l'accès au compteur et évitons la pollution du périmètre global avec des variables supplémentaires.

Mais que faire si nous avons besoin de flexibilité, si nous devons lire ou même modifier la valeur d'un tel compteur et ne pas obstruer la portée mondiale?

Bien sûr, vous pouvez créer une classe avec la variable correspondante et avec des méthodes qui vous permettent de travailler avec elle. Ou vous ne pouvez pas vous embêter avec de telles choses et simplement utiliser les propriétés des fonctions.

 //  ,   function generateNumber () { if (!generateNumber.counter) {   generateNumber.counter = 0 } return ++generateNumber.counter } console.log(generateNumber()) // 1 console.log(generateNumber()) // 2 console.log('current counter value: ', generateNumber.counter) // current counter value: 2 generateNumber.counter = 10 console.log('current counter value: ', generateNumber.counter) // current counter value: 10 console.log(generateNumber()) // 11 

Propriétés de l'objet Arguments


Je suis sûr que la plupart d'entre vous savent que les fonctions ont un objet arguments . Il s'agit d'un objet de type tableau accessible dans toutes les fonctions (à l'exception des fonctions fléchées, qui n'ont pas leur propre objet arguments ). Il contient une liste d'arguments passés à la fonction lors de son appel. De plus, il possède des propriétés intéressantes:

  • arguments.callee contient un lien vers la fonction actuelle.
  • arguments.caller contient une référence à la fonction qui a appelé la fonction actuelle.

Prenons un exemple.

 //  callee  caller  arguments const myFunction = function () { console.log('Current function: ', arguments.callee.name) console.log('Invoked by function: ', arguments.callee.caller.name) } void function main () { myFunction() } () // Current function: myFunction // Invoked by function: main 

La norme ES5 interdit l'utilisation des propriétés de l' caller et de l' caller en mode strict, mais elles sont encore largement présentes dans de nombreux textes de programme compilés par JavaScript, par exemple dans les bibliothèques. Par conséquent, il est utile de les connaître.

Littéraux de modèle balisés


Certes, si vous avez quelque chose à voir avec la programmation JavaScript, vous avez entendu parler des littéraux de modèle . Les littéraux de modèle sont l'une des nombreuses grandes innovations de la norme ES6. Cependant, connaissez-vous les littéraux de modèles balisés?

 //    `Hello ${username}!` //    myTag`Hello ${username}!` 

Les littéraux de modèle balisés permettent au développeur de contrôler la façon dont le littéral de modèle se transforme en chaîne. Cela se fait à l'aide de balises spéciales. Une balise n'est que le nom d'une fonction d'analyseur qui reçoit un tableau de chaînes et de valeurs interprétées par un modèle de chaîne. Lorsque vous utilisez une fonction de balise, il est prévu qu'elle renvoie la chaîne terminée.

Dans l'exemple suivant, notre balise, highlight , interprète les données d'un modèle littéral et incorpore ces données dans une ligne finie, en les plaçant dans la balise HTML <mark> pour la sélectionner lorsqu'un tel texte est affiché sur une page Web.

 //    function highlight(strings, ...values) { //  i -      let result = '' strings.forEach((str, i) => {   result += str   if (values[i]) {     result += `<mark>${values[i]}</mark>`   } }) return result } const author = 'Henry Avery' const statement = `I am a man of fortune & I must seek my fortune` const quote = highlight`${author} once said, ${statement}` // <mark>Henry Avery</mark> once said, <mark>I am a man of fortune // & I must seek my fortune</mark> 

Des façons intéressantes d'utiliser cette fonctionnalité peuvent être trouvées dans de nombreuses bibliothèques. Voici quelques exemples:

  • composants de style - pour une utilisation dans les applications React.
  • es2015-i18n-tag - pour la traduction et l'internationalisation de projets.
  • craie - pour la sortie de messages multicolores sur la console.

Getters and Setters dans ES5


Les objets JavaScript, pour la plupart, sont assez simples. Supposons que nous ayons un objet user et que nous essayions d'accéder à sa propriété age à l'aide de la construction user.age . Avec cette approche, si cette propriété est définie, nous obtiendrons sa valeur, et si elle n'est pas définie, nous obtiendrons undefined . Tout est très simple.

Mais travailler avec des propriétés ne doit pas du tout être aussi primitif. Les objets JS implémentent le concept de getters et setters. Au lieu de renvoyer directement la valeur d'une propriété de l'objet, nous pouvons écrire notre propre fonction getter, qui retourne ce que nous considérons nécessaire. La même chose s'applique à l'écriture de nouvelles valeurs dans les propriétés à l'aide des fonctions de définition.

Les getters et setters vous permettent d'implémenter des schémas avancés pour travailler avec les propriétés. Lors de la lecture ou de l'écriture de propriétés, vous pouvez utiliser les concepts de champs virtuels, vous pouvez vérifier les valeurs des champs et lors de l'écriture ou de la lecture, certains effets secondaires utiles peuvent se produire.

 //    const user = { firstName: 'Nathan', lastName: 'Drake', // fullname -    get fullName() {   return this.firstName + ' ' + this.lastName }, //      set age(value) {   if (isNaN(value)) throw Error('Age has to be a number')   this._age = Number(value) }, get age() {   return this._age } } console.log(user.fullName) // Nathan Drake user.firstName = 'Francis' console.log(user.fullName) // Francis Drake user.age = '29' console.log(user.age) // 29 // user.age = 'invalid text' // Error: Age has to be a number 

Les getters et setters ne sont pas des innovations standard ES5. Ils étaient toujours présents dans la langue. Dans ES5, seuls des outils de syntaxe pratiques ont été ajoutés pour fonctionner avec eux. Les détails sur les getters et setters peuvent être trouvés ici .

La bibliothèque populaire Node.js Colors est un exemple d'utilisation des getters.

Cette bibliothèque étend la classe String et y ajoute de nombreuses méthodes getter. Cela vous permet de convertir une chaîne dans sa version «colorée» afin que cette chaîne puisse ensuite être utilisée pour la journalisation. Cela se fait en travaillant avec les propriétés de chaîne.

Opérateur virgule


JS a un opérateur virgule. Il vous permet d'écrire plusieurs expressions sur une seule ligne, séparées par une virgule, et de retourner le résultat de l'évaluation de la dernière expression. Voici à quoi ressemblent de tels designs.

 let result = expression1, expression2,... expressionN 

Ici, les valeurs de toutes les expressions seront calculées, après quoi la valeur de expressionN entrera dans la variable de result .

Il est possible que vous ayez déjà utilisé l'opérateur virgule dans for boucles.

 for (var a = 0, b = 10; a <= 10; a++, b--) 

Parfois, cet opérateur est très utile lorsque vous devez écrire plusieurs expressions sur la même ligne.

 function getNextValue() {   return counter++, console.log(counter), counter } 

Il peut être utile lors de la conception de petites fonctions fléchées.

 const getSquare = x => (console.log (x), x * x) 

Opérateur Plus


Si vous devez rapidement transformer une chaîne en nombre, l'opérateur plus vous est utile. Il est capable de travailler avec une variété de nombres, et pas seulement, comme cela peut sembler, avec des nombres positifs. Nous parlons de nombres négatifs, octaux, hexadécimaux et de nombres en notation exponentielle. De plus, il est capable de convertir des objets Date et des objets de bibliothèque Moment.js en horodatages.

 //  "" +'9.11'          // 9.11 +'-4'            // -4 +'0xFF'          // 255 +true            // 1 +'123e-5'        // 0.00123 +false           // 0 +null            // 0 +'Infinity'      // Infinity +'1,234'         // NaN +new Date      // 1542975502981 ( ) +momentObject    // 1542975502981 ( ) 

Double point d'exclamation


Il est à noter que ce qu'on appelle parfois «l'opérateur du double point d'exclamation» (Bang Bang ou Double Bang) n'est pas, en fait, un opérateur. Il s'agit d'un opérateur NOT logique ou d'un opérateur de négation logique qui ressemble à un point d'exclamation répété deux fois. Le point d'exclamation double est bon car il vous permet de convertir n'importe quelle expression en valeur booléenne. Si l'expression, du point de vue de JS, est vraie - après l'avoir traitée avec un double point d'exclamation, true sera renvoyée. Sinon, false sera retourné.

 //     !!null            // false !!undefined       // false !!false           // false !!true            // true !!""              // false !!"string"        // true !!0               // false !!1               // true !!{}              // true !![]              // true 

Opérateur de négation au niveau du bit


Avouons-le: personne ne se soucie des opérateurs au niveau du bit. Je ne parle pas de les utiliser. Cependant, l'opérateur de négation au niveau du bit peut être utilisé dans de nombreuses situations.

Lorsque cet opérateur est appliqué aux nombres, il les convertit comme suit: à partir du nombre N s'avère -(N+1) . Une telle expression donne 0 si N est -1 .

Cette fonctionnalité peut être utilisée avec la méthode indexOf() lorsqu'elle est utilisée pour vérifier l'existence d'un élément dans un tableau ou dans une chaîne, car cette méthode, ne trouvant pas l'élément, renvoie -1 .

 //      indexOf let username = "Nathan Drake" if (~username.indexOf("Drake")) { console.log('Access denied') } else { console.log('Access granted') } 

Il convient de noter que dans les normes ES6 et ES7, respectivement, pour les chaînes et les tableaux, la méthode includes() est apparue. C'est certainement beaucoup plus pratique pour déterminer la présence d'éléments que d'utiliser l'opérateur de négation au niveau du bit et indexOf() .

Blocs nommés


JavaScript a un concept d'étiquettes, à l'aide duquel vous pouvez attribuer des noms (étiquettes) aux boucles. Vous pouvez ensuite utiliser ces étiquettes pour faire référence à la boucle appropriée lorsque vous appliquez des instructions break ou continue . Les étiquettes peuvent également être affectées à des blocs de code standard.

Les boucles étiquetées sont utiles lorsque vous travaillez avec des boucles imbriquées. Mais ils peuvent également être utilisés pour organiser facilement le code en blocs ou lors de la création de blocs dans lesquels le code peut être interrompu.

 //    declarationBlock: { //       //     var i, j } forLoop1: //     - "forLoop1" for (i = 0; i < 3; i++) {       forLoop2: //     -  "forLoop2"  for (j = 0; j < 3; j++) {       if (i === 1 && j === 1) {        continue forLoop1     }     console.log('i = ' + i + ', j = ' + j)  } } /* i = 0, j = 0 i = 0, j = 1 i = 0, j = 2 i = 1, j = 0 i = 2, j = 0 i = 2, j = 1 i = 2, j = 2 */ //      loopBlock4: { console.log('I will print') break loopBlock4 console.log('I will not print') } // I will print 

Notez que, contrairement à certaines autres langues, il n'y a aucune goto dans JS. Par conséquent, les étiquettes sont utilisées uniquement avec les instructions break et continue .

Résumé


Dans cet article, nous avons parlé de fonctionnalités JavaScript peu connues, dont la connaissance est utile à tout programmeur JS, au moins pour être prêt à rencontrer quelque chose d'inhabituel dans le code de quelqu'un d'autre. Si le sujet de «JS inconnu» vous intéresse, vous pouvez jeter un œil à notre publication.

Chers lecteurs! Si vous connaissez des fonctionnalités peu connues de JS et voyez des options pour leur application pratique, veuillez nous en parler.

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


All Articles