Quels personnages sont regroupés avec Array.from?


38

J'ai joué avec JS et je n'arrive pas à comprendre comment JS décide quels éléments ajouter au tableau créé lors de l'utilisation Array.from(). Par exemple, l'emoji 👍 suivant a un lengthde 2, car il est composé de deux points de code, mais Array.from()traite ces deux points de code comme un, donnant un tableau avec un élément:

const emoji = '👍';
console.log(Array.from(emoji)); // Output: ["👍"]

Cependant, certains autres caractères ont également deux points de code tels que ce caractère षि(a également un .lengthde 2). Cependant, Array.fromne "groupe" pas ce personnage et produit à la place deux éléments:

const str = 'षि';
console.log(Array.from(str)); // Output: ["ष", "ि"]

Ma question est la suivante: qu'est-ce qui détermine si le caractère est divisé (comme dans l'exemple deux) ou traité comme un seul élément (comme dans l'exemple un) lorsque le caractère se compose de deux points de code?


5
Jetez un œil aux paires de substitution UTF-16 ...
Jonas Wilms


1
Je crains au sujet de polyfill de Array.from de MDN, qui a un comportement différent: -s
Ele

1
@Ele il ne considère que les objets avec length. Les itérateurs ou même Setne fonctionnent pas avec ça
adiga

Réponses:


26

Array.fromessaie d'abord d'appeler l'itérateur de l'argument s'il en a un, et les chaînes ont des itérateurs, donc il invoque String.prototype[Symbol.iterator], alors regardons comment fonctionne la méthode prototype. Il est décrit dans la spécification ici :

  1. Soit O? RequireObjectCoercible (cette valeur).
  2. Soyons ? ToString (O).
  3. Renvoie CreateStringIterator (S).

La recherche vous CreateStringIteratoramène finalement à 21.1.5.2.1 %StringIteratorPrototype%.next ( ), ce qui:

  1. Que cp soit! CodePointAt (s, position).
  2. Soit resultString la valeur de chaîne contenant cp. [[CodeUnitCount]] unités de code consécutives de s commençant par l'unité de code à la position d'index.
  3. Définissez O. [[StringNextIndex]] sur position + cp. [[CodeUnitCount]].
  4. Renvoie CreateIterResultObject (resultString, false).

C'est CodeUnitCountce qui vous intéresse. Ce numéro provient de CodePointAt :

  1. Soit d'abord l'unité de code à la position d'index dans la chaîne.
  2. Soit cp le point de code dont la valeur numérique est celle du premier.
  3. Si le premier n'est pas un substitut principal ou un substitut de fuite, alors

    une. Renvoyez le dossier { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.

  4. Si le premier est un substitut de fin ou une position + 1 = taille, alors

    a.Retournez l'enregistrement { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  5. Soit en second lieu l'unité de code à la position d'index + 1 dans la chaîne.

  6. Si le second n'est pas un substitut de fuite, alors

    une. Renvoyez le dossier { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  7. Réglez cp sur! UTF16DecodeSurrogatePair (premier, deuxième).

  8. Renvoyez le dossier { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.

Ainsi, lors de l'itération sur une chaîne avec Array.from, il renvoie un CodeUnitCount de 2 uniquement lorsque le caractère en question est le début d'une paire de substitution. Les caractères qui sont interprétés comme des paires de substitution sont décrits ici :

De telles opérations appliquent un traitement spécial à chaque unité de code avec une valeur numérique dans la plage inclusive 0xD800 à 0xDBFF (définie par la norme Unicode comme substitut principal , ou plus formellement comme unité de code à substitution élevée) et à chaque unité de code avec une valeur numérique dans la plage inclusive 0xDC00 à 0xDFFF (défini comme un substitut de fin, ou plus formellement comme une unité de code à faible substitution) en utilisant les règles suivantes ..:

षि n'est pas une paire de substitution:

console.log('षि'.charCodeAt()); // First character code: 2359, or 0x937
console.log('षि'.charCodeAt(1)); // Second character code: 2367, or 0x93F

Mais 👍les personnages de sont:

console.log('👍'.charCodeAt()); // 55357, or 0xD83D
console.log('👍'.charCodeAt(1)); // 56397, or 0xDC4D

Le premier code de caractère de '👍'est, en hexadécimal, D83D, qui est dans la gamme 0xD800 to 0xDBFFdes principaux substituts. En revanche, le premier code de caractère de 'षि'est beaucoup plus bas, et ne l'est pas. Alors le'षि' se sépare, mais '👍'ne le fait pas.

षिest composé de deux caractères distincts: , Lettre Devanagari Ssa , et ि, Devanagari Voyelle Connectez - je . Lorsqu'ils sont côte à côte dans cet ordre, ils sont combinés graphiquement en un seul caractère visuellement, bien qu'ils soient composés de deux caractères distincts.

En revanche, les codes de caractère 👍 n'ont de sens que lorsqu'ils sont ensemble comme un seul glyphe. Si vous essayez d'utiliser une chaîne avec l'un des points de code sans l'autre, vous obtiendrez un symbole non-sens:

console.log('👍'[0]);
console.log('👍'[1]);


10
Je pense que, bien que généralement correcte, utile et avec des citations soigneusement fournies, cette réponse ne parvient pas à expliquer clairement la différence clé entre les deux cas: du point de vue Unicode, il षिs'agit en fait de deux caractères avec des points de code distincts combinés pour former un seul glyphe (un caractère abstrait , tel que compris par les humains). Cela contraste avec l' 👍emoji, qui est un caractère complet en soi, même si son point de code est suffisamment élevé pour être divisé en une paire de substitution. Je pense que clarifier cela pourrait aider cette réponse (autrement utile) à beaucoup.
rhino

Plus précisément, la consonne ष (ṣ) et la voyelle ि (i) se combinant graphiquement dans la syllabe षि (ṣi)
Amadan

@CertainPerformance Il n'y a qu'un seul point de code dans "👍". Cela suggère que la terminologie de cette réponse est peut-être incorrecte.
Ben Aston

13

UTF-16 (l'encodage utilisé pour les chaînes en js) utilise des unités 16 bits. Ainsi, chaque unicode qui peut être représenté à l'aide de 15 bits est représenté comme un point de code, tout le reste comme deux, appelés paires de substitution . L' itérateur de chaînes parcourt les points de code.

UTF-16 sur Wikipédia


8

Tout dépend du code derrière les personnages. Certains sont codés sur deux octets (UTF-16) et sont interprétés par Array.fromdeux caractères. Je dois vérifier la liste des personnages:

http://www.fileformat.info/info/charset/UTF-8/list.htm

http://www.fileformat.info/info/charset/UTF-16/list.htm

function displayHexUnicode(s) {
  console.log(s.split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(4,"0"),""));
}

displayHexUnicode('षि');

console.log(Array.from('षि').forEach(x => displayHexUnicode(x)));


function displayHexUnicode(s) {
  console.log(s.split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(4,"0"),""));
}

displayHexUnicode('👍');

console.log(Array.from('👍').forEach(x => displayHexUnicode(x)));


Pour la fonction qui affiche le code hexadécimal:

Javascript: chaîne Unicode en hexadécimal

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.