Description du problème
Certaines chaînes vides "" apparaissent lors de l'utilisation de la méthode split de JavaScript pour diviser une chaîne, en particulier lors de l'utilisation d'expressions régulières comme délimiteurs.
Questions connexes
L'expression régulière Javascript produit un groupe de chaînes vide lors du regroupement de chaînes ?
Dans la question ci-dessus, lorsque le sujet a utilisé des expressions régulières pour diviser la chaîne, plusieurs chaînes vides "" ont été générées. Le code est le suivant :
Alors, quelle est la raison de ces chaînes vides ?
Analyse du problème
Après une recherche sur Google, j'ai découvert qu'il n'y avait pas beaucoup de résultats associés, et même s'il y en avait, il n'y avait pas beaucoup d'explications détaillées. J'ai donné une brève introduction puis j'ai donné un lien vers la spécification ECMAScript. Il semble que si vous voulez connaître la vraie raison, vous devez serrer les dents et lire le règlement.
Normes associées
Ensuite, conformément à la pratique internationale, commençons par la mairie standard d'ECMAScript.
Ce chapitre présente en détail les étapes d'exécution de la méthode split. Si vous êtes intéressé, vous pouvez le lire attentivement étape par étape. Je n'expliquerai ici que les étapes liées à la génération d'une chaîne vide. tout le monde est le bienvenu proposer.
Étapes associées
Extraire quelques étapes :
L'étape la plus importante de tout le processus est le cycle de l'étape 13, et les principales tâches de ce cycle sont les suivantes :
•Définir les valeurs de p et q. Les valeurs de p et q sont les mêmes au début de chaque boucle (cette étape est en dehors de la boucle
) ;
•Appelez la méthode SplitMatch(S, q, R) pour diviser la chaîne
;
•Selon les différents résultats renvoyés, différentes branches sont exécutées, et la branche principale est la branche ⅲ
;
•La branche ⅲ est divisée en 8 petites étapes pour remplir les résultats renvoyés dans le tableau prédéfini A
•Dans ces 8 petites étapes, la fonction de l'étape 1 est de renvoyer une sous-chaîne de la chaîne d'origine. La position de départ est p (inclus) et la position de fin est q (non inclus). Remarque : Dans cette étape, une chaîne vide est. généré, que je marque comme une chaîne tronquée pour une référence facile ci-dessous.
•Ajoutez la sous-chaîne de l'étape précédente au tableau A
•Les prochaines étapes consistent à mettre à jour les variables pertinentes et à poursuivre le cycle suivant. (La fonction de l'étape 7 est de sauvegarder le groupe de capture dans l'expression régulière dans le tableau A, et n'a rien à voir avec la génération d'une chaîne vide)
SplitMatch(S, q, R)
Ensuite, nous devons comprendre ce que fait la méthode SplitMatch(S, q, R). Cette méthode est mentionnée plus bas dans la spécification divisée. Ce qu'il fait principalement, c'est effectuer les opérations correspondantes selon le type de séparateur :
•Si le délimiteur est de type RegExp, appelez la méthode interne [[Match]] de RegExp pour faire correspondre la chaîne. Si la correspondance échoue, un échec est renvoyé. Sinon, un résultat de type MatchResult est renvoyé.
•Si le séparateur est une chaîne, la correspondance est jugée, un échec est renvoyé et un résultat de type MatchResult est renvoyé avec succès.
Résultat du match
Les étapes ci-dessus introduisent une autre variable de type MatchResult. En vérifiant la documentation, nous avons constaté que ce type de variable a deux attributs, endIndex et captures. La valeur de endIndex est la position correspondant à la chaîne plus 1. Les captures peuvent être comprises comme un tableau lorsque le délimiteur est une expression régulière, les éléments. il s'agit de la valeur capturée par le groupe ; lorsque le délimiteur est une chaîne, il s'agit d'un tableau vide.
Suivant
Nous pouvons voir à partir des étapes ci-dessus que la chaîne divisée est générée lors de l'étape d'interception de la chaîne (sauf pour la capture de groupe des expressions régulières). Sa fonction est d'intercepter la chaîne entre la position de début (incluse) et la position de fin spécifiées (non incluses). Alors, quand renverra-t-il "" ? Il existe un cas particulier où les valeurs de la position de départ et de la position finale sont égales. Ceci n'est qu'une supposition, car la spécification ne donne pas les étapes standard pour intercepter la chaîne.
Nous sommes arrivés jusqu’ici, pourquoi ne pas faire un pas de plus ?
J'ai donc essayé de rechercher du code source de la V8 pour voir si je pouvais trouver une méthode d'implémentation spécifique. J'ai trouvé le code pertinent, lien du code source
Voici un extrait de l'un d'eux :
if (limit === 0) return [];
// ECMA-262 dit que si le séparateur n'est pas défini, le résultat devrait
// soit un tableau de taille 1 contenant la chaîne entière.
Si (IS_UNDEFINED(separator)) renvoie [sujet];
var separator_length = separator_string.length;
//Le délimiteur est une chaîne vide, et le tableau de caractères est renvoyé directement
Si (separator_length === 0) return %StringToArray(subject, limit);
var result = %StringSplit(subject, separator_string, limit);
résultat de retour ;
>
if (limit === 0) return [];
// Lorsque le séparateur est une expression régulière, appelez StringSplitOnRegExp
Renvoie StringSplitOnRegExp(sujet, séparateur, limite, longueur);
>
//Omettez certains codes ici
J'ai trouvé dans le code que la méthode %_SubString est appelée pour intercepter la chaîne lors du remplissage du tableau. Malheureusement, je n'ai pas trouvé sa définition pertinente. Si vous la trouvez, faites-le moi savoir. Cependant, j'ai trouvé que la méthode StringSubstring correspondant à la méthode substring en JavaScript appellera la méthode %_SubString et renverra le résultat. Ensuite, si 'abc'.substring(1,1) renvoie "", cela signifie que la méthode %_SubString retournera "" lorsque la position de début et la position de fin sont les mêmes. Vous connaîtrez le résultat après l'avoir essayé.
Alors, quand la position de départ sera-t-elle égale à la position d'arrivée (c'est-à-dire q === p) ? J'ai analysé étape par étape selon les étapes ci-dessus, et j'ai finalement trouvé :
•Une fois que la chaîne d'origine S correspond une fois au délimiteur, la position suivante de la chaîne S correspond également au délimiteur. Tels que : 'abbbc'.split('b'), 'abbbc'.split(/(b){1}/)
•Une autre situation est lorsqu'un ou plusieurs caractères au début de la chaîne correspondent au délimiteur. Tels que : 'abc'.split('a'), 'abc'.split(/ab/)
•Il existe également un cas où une ou plusieurs chaînes à la fin de la chaîne correspondent au délimiteur, et l'étape associée est l'étape 14.
Tels que : 'abc'.split('c'), 'abc'.split(/bc/)
De plus, lors de l'utilisation d'expressions régulières comme délimiteurs, undefined peut apparaître dans les résultats renvoyés.
Tel que : 'abc'.split(/(d)*/)
Retournez en arrière et regardez l'exemple du début. Est-ce qu'il remplit les conditions ci-dessus ?
Digression
C'est la première fois que je lis si attentivement la spécification standard ECMAScript. Le processus de lecture est en effet douloureux, mais après l'avoir compris, je me sens très heureux. Merci également à l'intervenant d'avoir soulevé cette question et d'avoir donné suite.
D’ailleurs, lorsqu’une expression régulière est utilisée comme délimiteur, le modificateur global g sera ignoré, ce qui constitue un gain supplémentaire.