Connaissances avancées
La section approfondit les éléments internes des chaînes. Ces connaissances vous seront utiles si vous envisagez de traiter avec des emoji, des caractères mathématiques ou hiéroglyphiques rares ou d'autres symboles rares.
Comme nous le savons déjà, les chaînes JavaScript sont basées sur Unicode : chaque caractère est représenté par une séquence d'octets de 1 à 4 octets.
JavaScript nous permet d'insérer un caractère dans une chaîne en spécifiant son code Unicode hexadécimal avec l'une de ces trois notations :
xXX
XX
doit être composé de deux chiffres hexadécimaux avec une valeur comprise entre 00
et FF
, alors xXX
est le caractère dont le code Unicode est XX
.
Étant donné que la notation xXX
ne prend en charge que deux chiffres hexadécimaux, elle ne peut être utilisée que pour les 256 premiers caractères Unicode.
Ces 256 premiers caractères incluent l’alphabet latin, la plupart des caractères de syntaxe de base et quelques autres. Par exemple, "x7A"
est identique à "z"
(Unicode U+007A
).
alerte( "x7A" ); // z alerte( "xA9" ); // ©, le symbole du droit d'auteur
uXXXX
XXXX
doit contenir exactement 4 chiffres hexadécimaux avec une valeur comprise entre 0000
et FFFF
, alors uXXXX
est le caractère dont le code Unicode est XXXX
.
Les caractères avec des valeurs Unicode supérieures à U+FFFF
peuvent également être représentés avec cette notation, mais dans ce cas, nous devrons utiliser ce qu'on appelle une paire de substitution (nous parlerons des paires de substitution plus tard dans ce chapitre).
alerte( "u00A9" ); // ©, identique à xA9, en utilisant la notation hexadécimale à 4 chiffres alerte( "u044F" ); // я, la lettre de l'alphabet cyrillique alerte( "u2191" ); // ↑, le symbole de la flèche vers le haut
u{X…XXXXXX}
X…XXXXXX
doit être une valeur hexadécimale de 1 à 6 octets comprise entre 0
et 10FFFF
(le point de code le plus élevé défini par Unicode). Cette notation nous permet de représenter facilement tous les caractères Unicode existants.
alert( "u{20331}" ); // 佫, un caractère chinois rare (long Unicode) alerte( "u{1F60D}" ); // ?, un symbole de visage souriant (un autre long Unicode)
Tous les caractères fréquemment utilisés ont des codes à 2 octets (4 chiffres hexadécimaux). Les lettres de la plupart des langues européennes, les chiffres et les ensembles idéographiques unifiés de base CJK (CJK – des systèmes d'écriture chinois, japonais et coréen) ont une représentation sur 2 octets.
Initialement, JavaScript était basé sur le codage UTF-16 qui n'autorisait que 2 octets par caractère. Mais 2 octets n'autorisent que 65 536 combinaisons et cela ne suffit pas pour tous les symboles Unicode possibles.
Ainsi, les symboles rares qui nécessitent plus de 2 octets sont codés avec une paire de caractères de 2 octets appelée « paire de substitution ».
Comme effet secondaire, la longueur de ces symboles est 2
:
alert( '?'.length ); // 2, ÉCRITURE MATHÉMATIQUE MAJUSCULE X alert( '?'.length ); // 2, VISAGE AUX LARMES DE JOIE alert( '?'.length ); // 2, un caractère chinois rare
En effet, les paires de substitution n'existaient pas au moment de la création de JavaScript et ne sont donc pas correctement traitées par le langage !
Nous avons en fait un seul symbole dans chacune des chaînes ci-dessus, mais la propriété length
affiche une longueur de 2
.
Obtenir un symbole peut également être délicat, car la plupart des fonctionnalités du langage traitent les paires de substitution comme deux caractères.
Par exemple, nous pouvons voir ici deux caractères impairs dans la sortie :
alerte( '?'[0] ); // affiche des symboles étranges... alerte( '?'[1] ); // ...morceaux de la paire de substitution
Les morceaux d’une paire de substitution n’ont aucune signification les uns sans les autres. Ainsi, les alertes de l’exemple ci-dessus affichent en fait des déchets.
Techniquement, les paires de substitution sont également détectables par leurs codes : si un caractère a le code dans l'intervalle de 0xd800..0xdbff
, alors c'est la première partie de la paire de substitution. Le caractère suivant (deuxième partie) doit avoir le code dans l'intervalle 0xdc00..0xdfff
. Ces intervalles sont réservés exclusivement aux paires de substitution par la norme.
Ainsi, les méthodes String.fromCodePoint et str.codePointAt ont été ajoutées en JavaScript pour gérer les paires de substitution.
Ils sont essentiellement les mêmes que String.fromCharCode et str.charCodeAt, mais ils traitent correctement les paires de substitution.
On peut voir la différence ici :
// charCodeAt ne prend pas en compte les paires de substitution, il donne donc les codes pour la 1ère partie de ? : alert( '?'.charCodeAt(0).toString(16) ); //d835 // codePointAt prend en compte les paires de substitution alert( '?'.codePointAt(0).toString(16) ); // 1d4b3, lit les deux parties de la paire de substitution
Cela dit, si on prend de la position 1 (et c'est plutôt incorrect ici), alors ils ne renvoient tous les deux que la 2ème partie de la paire :
alert( '?'.charCodeAt(1).toString(16) ); //dcb3 alert( '?'.codePointAt(1).toString(16) ); //dcb3 // 2ème moitié de la paire dénuée de sens
Vous trouverez d'autres façons de gérer les paires de substitution plus loin dans le chapitre Iterables. Il existe probablement des bibliothèques spéciales pour cela aussi, mais rien d'assez célèbre pour être suggéré ici.
À retenir : diviser des chaînes à un point arbitraire est dangereux
Nous ne pouvons pas simplement diviser une chaîne à une position arbitraire, par exemple prendre str.slice(0, 4)
et nous attendre à ce que ce soit une chaîne valide, par exemple :
alert( 'salut ?'.slice(0, 4) ); // Salut [?]
Ici, nous pouvons voir un caractère poubelle (première moitié de la paire de substituts du sourire) dans la sortie.
Soyez-en conscient si vous avez l'intention de travailler de manière fiable avec des paires de substitution. Ce n’est peut-être pas un gros problème, mais au moins vous devriez comprendre ce qui se passe.
Dans de nombreuses langues, il existe des symboles composés du caractère de base avec une marque au-dessus/en dessous.
Par exemple, la lettre a
peut être le caractère de base de ces caractères : àáâäãåā
.
Les caractères « composites » les plus courants ont leur propre code dans le tableau Unicode. Mais pas toutes, car il y a trop de combinaisons possibles.
Pour supporter des compositions arbitraires, le standard Unicode permet d'utiliser plusieurs caractères Unicode : le caractère de base suivi d'un ou plusieurs caractères « marque » qui le « décorent ».
Par exemple, si nous avons S
suivi du caractère spécial « point au-dessus » (code u0307
), il est affiché comme Ṡ.
alerte( 'Su0307' ); //Ṡ
Si nous avons besoin d’une marque supplémentaire au-dessus de la lettre (ou en dessous) – pas de problème, ajoutez simplement le caractère de marque nécessaire.
Par exemple, si nous ajoutons un caractère « point en dessous » (code u0323
), alors nous aurons « S avec des points au-dessus et en dessous » : Ṩ
.
Par exemple:
alerte( 'Su0307u0323' ); //Ṩ
Cela offre une grande flexibilité, mais pose également un problème intéressant : deux caractères peuvent se ressembler visuellement, mais être représentés avec des compositions Unicode différentes.
Par exemple:
soit s1 = 'Su0307u0323'; // Ṩ, S + point au dessus + point en dessous soit s2 = 'Su0323u0307'; // Ṩ, S + point en dessous + point au dessus alerte( `s1 : ${s1}, s2 : ${s2}` ); alerte( s1 == s2 ); // faux bien que les caractères semblent identiques (?!)
Pour résoudre ce problème, il existe un algorithme de « normalisation Unicode » qui ramène chaque chaîne à la forme « normale » unique.
Il est implémenté par str.normalize().
alert( "Su0307u0323".normalize() == "Su0323u0307".normalize() ); // vrai
C'est drôle que dans notre situation normalize()
rassemble en fait une séquence de 3 caractères en un : u1e68
(S avec deux points).
alert( "Su0307u0323".normalize().length ); // 1 alert( "Su0307u0323".normalize() == "u1e68" ); // vrai
En réalité, ce n’est pas toujours le cas. La raison en est que le symbole Ṩ
est « assez commun », c'est pourquoi les créateurs d'Unicode l'ont inclus dans le tableau principal et lui ont donné le code.
Si vous souhaitez en savoir plus sur les règles et variantes de normalisation, elles sont décrites dans l'annexe de la norme Unicode : Formulaires de normalisation Unicode, mais pour la plupart des besoins pratiques, les informations de cette section sont suffisantes.