Article original : How does String.padStart actually work?

Par Yazeed Bzadough

Précédemment, j'ai partagé mon utilisation de padStart pour remplacer élégamment ce qui aurait été des tonnes de déclarations if. Cette méthode magique m'a laissé sans voix. Je ne pouvais tout simplement pas croire qu'elle existait.

Ce qu'elle fait

Documentation Mozilla Developer Network (MDN):

La méthode padStart() complète la chaîne actuelle avec une autre chaîne (répétée, si nécessaire) de sorte que la chaîne résultante atteigne la longueur donnée. Le remplissage est appliqué à partir du début (à gauche) de la chaîne actuelle.

Continuer à préfixer une chaîne à une autre chaîne jusqu'à ce que la longueur cible soit atteinte.

1*XgjBHs6faLKurpx6WOxmaQ1*kvWWV9-Le3akATlMGLFIUA

Si la longueur est déjà inférieure à la longueur de la chaîne originale, rien ne se passe.

1*tmVv1tdy9Ye099ca2YBD4w

Et puisque padStart retourne une chaîne, nous pouvons enchaîner ses méthodes.

1*LhQzpSiSSlvTDkcHyL8HtA

Vous voyez ? 1, 2, 3, 4 et 5 sont tous inférieurs ou égaux à la longueur de world qui est de 5, donc padStart ne fait rien.

Support des navigateurs

Malheureusement, le support est actuellement "meh"

1*OsJkuMt7gxC407zlxv1ImwSupport desktop1*dtwqtBR1j9vDDi2AkpP61QSupport mobile

Vous pouvez soit utiliser babel-polyfill ou le polyfill de MDN.

Voici le polyfill de MDN.

1*Zf4kxLLpi3CsYN94Nl4axQ

Quelques points d'intérêt :

  • Prototypes (lignes 1 et 2)
  • Opérateurs bit à bit (ligne 4)
  • padString.repeat (ligne 14)
  • padString.slice (ligne 17)

Je suis prêt à les passer en revue si vous êtes intéressés ?

Les lignes 1 et 2 ne sont pas si mauvaises : "Si padStart n'est pas supporté par le navigateur, créons notre propre padStart et ajoutons-le" (c'est le polyfill en résumé).

Une manière courante de vérifier le support d'une méthode par le navigateur est d'inspecter le prototype de son objet. Puisque padStart est une méthode de chaîne, elle devrait exister sur String.prototype.

Ma vieille version de Safari ne supporte pas padStart.

1*8zmT7mTVUn2Q4MqunHXicgSupport de padStart dans mon Safari

Mais mon Chrome et Firefox le supportent.

1*paNRJ_6YQ9ThHHxkwEpZwASupport de padStart dans Chrome1*jn3Exskqn_8EAQORGs_FKgSupport de padStart dans Firefox

Considérez cette vérification de sécurité à la ligne 1

if (!String.prototype.padStart) {
}

Cette déclaration if ne retournerait true que dans mon vieux Safari. Elle retourne false dans Chrome/Firefox, donc aucun polyfill n'est appliqué.

1*Zf4kxLLpi3CsYN94Nl4axQ

Passons à la ligne 2, qui crée une nouvelle fonction appelée padStart et l'assigne à String.prototype.padStart. Grâce au modèle d'héritage de JavaScript, toute chaîne créée par la suite peut utiliser padStart.

Cette fonction prend deux paramètres

  1. targetLength : Quelle doit être la longueur de la chaîne résultante ?

  2. padString : Avec quoi la complétons-nous ?

Ajoutons des déclarations debugger à ce code.

1*ttFL0luCSlQzdyOh-lpzOA

J'ai également supprimé cette déclaration if de la ligne 1, donc même le String.prototype.padStart natif sera remplacé par cette fonction—ce qui est utile si vous voulez déboguer dans Chrome.

Ne remplacez pas les prototypes en production, les enfants !

1*srYXzRnU1Qt46J3x91vKjQ

En utilisant notre exemple initial

'world'.padStart(11, 'hello ');

1*lFrlt-xxEwyByiesDNqHpw

Regardez la ligne 2. Nous voyons que targetLength et padString ont trouvé leur chemin dans notre fonction. Pas de folie pour l'instant, mais ça arrive. J'ai évité la ligne 5 assez longtemps.

Opérateurs bit à bit

Le commentaire au-dessus de la ligne 5 décrit brièvement son but : "Si targetLength est un nombre, arrondissez-le à l'entier inférieur. Si ce n'est pas un nombre, mettez-le à 0".

Les opérateurs bit à bit rendent cela possible.

targetLength >> 0;

Cet opérateur >> est connu comme un déplacement binaire à droite avec propagation du signe (LOLWUT?). Vous l'utilisez avec deux nombres

a >> b

Ce que cela fait :

  1. a est converti en binaire (détails ici).
  2. Le binaire a est déplacé à droite b fois.

Notre targetLength est 11—ce qui est 1011 en binaire (voici un convertisseur si vous ne me croyez pas ?).

Un effet secondaire de la conversion en binaire est que les nombres sont arrondis à l'entier inférieur et que la plupart des non-nombres deviennent 0.

Essayez les exemples suivants

1*G9R342JuTLzAhZ3zXB5qYw

Vous voyez ? Les fractions deviennent des nombres entiers. Les non-nombres deviennent 0, avec une exception notable...

1*S5QRnVnjsJaP6LSR-f1yVg

Le binaire n'est que des 1 et des 0, n'est-ce pas ? Ces 1 et 0 représentent des interrupteurs "on" et "off"—true et false. La forme binaire de true est 1, et celle de false est 0. Gardez cela à l'esprit.

Maintenant que nous avons "sanitisé" targetLength, nous commençons le déplacement à droite.

Le déplacement à droite signifie que vous déplacez chaque bit à droite n fois. C'est tout.

Voici une visualisation PowerPoint de 11 >> 1 (J'avais oublié à quel point PowerPoint est génial).

1*jANUTARhf9DaSPo_FdobsQ

Transformez 11 en 1011 et déplacez-le à droite 1 fois. Votre résultat final est 101, qui est 5 en binaire.

1*hWhIMjgIzV8HsBHsoqaXkw

Mais notre code dit targetLength >> 0.

Donc nous déplaçons à droite 0 fois...

Le but de déplacer à droite 0 fois est d'abuser de cet effet secondaire de la conversion de targetLength en binaire. Nous ne voulons pas vraiment déplacer quoi que ce soit car cela changerait la valeur.

Continuons

1*9fp5LQLp8M02XXNypggPAw

Passez maintenant au debugger de la ligne 7. targetLength a été sanitisé. Suivant !

1*5olkuOlk90Alu9tVfbjS9Q

Ligne 11.

padString = String(padString || ' ');

Si nous ne fournissons pas d'argument padString, il est par défaut un espace vide. Je ne l'avais jamais remarqué jusqu'à maintenant.

1*esccGoVlxpemIBmMunjmXA

Ligne 17.

Remarquez comment la ligne 13 avait une autre vérification de sécurité, "Si la longueur de la chaîne originale est supérieure à targetLength, ne faites rien. Retournez simplement la chaîne originale"

Cela a du sens car si notre targetLength est 1, mais que la chaîne fait déjà 10 caractères, quel est l'intérêt ? Nous l'avons démontré plus tôt avec

// retourne simplement 'world'
'world'.padStart(0, 'hello ');

La ligne 18 détermine combien de caractères supplémentaires nous avons besoin en soustrayant targetLength de la longueur de la chaîne originale. Nous avons besoin de 6, dans ce cas.

1*fNa4w2qk360VICQLqvp6jQ

Ligne 27.

Nous avons sauté cette déclaration if à la ligne 20 parce que targetLength et padString.length se sont avérés être les mêmes, mais nous y reviendrons bientôt.

Pour l'instant, nous sommes arrêtés juste avant la ligne 29. Décomposons-la.

padString.slice(0, targetLength);

La bonne vieille méthode String.prototype.slice.

Documentation MDN:

La méthode slice() extrait une section d'une chaîne et la retourne en tant que nouvelle chaîne.

Elle est basée sur l'index, donc nous commençons à l'index 0 de padString, et nous prenons le nombre de caractères égal à targetLength. C'est un peu comme

1*5fgldncMrn1M42TDNexc5w

Retournez cette padString découpée combinée avec la chaîne originale, et vous avez terminé !

1*dPcP4geY5bM3H_Qu53rF3Q

Presque terminé

Normalement, je conclurais ici, mais nous n'avons pas exploré cette déclaration if à la ligne 20. Pour nous assurer de l'atteindre cette fois, essayons un autre exemple précédent

'yo'.padStart(20, 'yo');

1*xMe4-5cz9E4TcaxRV-OpCw

J'ai sauté à la ligne 20 parce que nous savons déjà ce qui se passe jusqu'à ce point.

if (targetLength > padString.length)

targetLength est 18, et padString est 'yo', avec une longueur de 2. 18 > 2, donc quoi ensuite ?

padString += padString.repeat(targetLength / padString.length);

Rappelez-vous, padStart retourne une padString découpée + la chaîne originale. Si vous voulez compléter 'yo' avec 'yo' jusqu'à ce qu'il fasse 20 caractères de long, vous devrez le répéter plusieurs fois. C'est là que cette logique se produit, en utilisant padString.repeat.

Documentation MDN:

La méthode repeat() construit et retourne une nouvelle chaîne qui contient le nombre spécifié de copies de la chaîne sur laquelle elle a été appelée, concatenées ensemble.

Donc, elle copie/colle la chaîne n fois.

Afin de déterminer combien de répétitions nous avons besoin, divisez targetLength par padString.length.

1*8uNfkR56h7AhooHJILFSJQ

Répétez 'yo' 9 fois et obtenez une chaîne de 'yo' qui fait 18 caractères de long. Ajoutez cela à votre 'yo' original, et votre compte final est de 20 caractères.

1*0A9siQbKWnKn6cFuidfNMQ

Mission accomplie. À la prochaine !