Quali personaggi sono raggruppati con Array.from?


38

Ho giocato con JS e non riesco a capire come JS decida quali elementi aggiungere all'array creato durante l'utilizzo Array.from(). Ad esempio, la seguente emoji 👍 ha un length2, poiché è composta da due punti di codice, ma Array.from()tratta questi due punti di codice come uno, dando una matrice con un elemento:

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

Tuttavia, alcuni altri personaggi hanno anche due punti di codice come questo personaggio षि(ha anche un .length2). Tuttavia, Array.fromnon "raggruppa" questo personaggio e produce invece due elementi:

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

La mia domanda è: cosa determina se il personaggio è suddiviso (come nell'esempio due) o trattato come un singolo elemento (come nell'esempio uno) quando il personaggio è costituito da due punti di codice?


5
Dai un'occhiata alle coppie surrogate UTF-16 ...
Jonas Wilms,


1
Ho una preoccupazione per il polyfill di MDN di Array.from, che ha un comportamento diverso: -s
Ele

1
@Ele considera solo gli oggetti con length. Iteratori o addirittura Setnon funziona con questo
adiga il

Risposte:


26

Array.fromcerca prima di invocare l'iteratore dell'argomento se ne ha uno e le stringhe hanno iteratori, quindi invoca String.prototype[Symbol.iterator], quindi cerchiamo di capire come funziona il metodo prototipo. È descritto nelle specifiche qui :

  1. Let O be? RequireObjectCoercible (questo valore).
  2. Facciamo ? ToString (O).
  3. Restituisce CreateStringIterator (S).

Alzare lo sguardo alla CreateStringIteratorfine ti porta a 21.1.5.2.1 %StringIteratorPrototype%.next ( ), che fa:

  1. Lascia che cp sia! CodePointAt (s, position).
  2. Lascia che resultString sia il valore String che contiene cp. [[CodeUnitCount]] unità di codice consecutive da s che iniziano con l'unità di codice in posizione indice.
  3. Impostare O. [[StringNextIndex]] su position + cp. [[CodeUnitCount]].
  4. Restituisce CreateIterResultObject (resultString, false).

Questo CodeUnitCountè ciò che ti interessa. Questo numero proviene da CodePointAt :

  1. Sia prima l'unità di codice nella posizione dell'indice all'interno della stringa.
  2. Sia cp il punto di codice il cui valore numerico è quello del primo.
  3. Se il primo non è un surrogato principale o un surrogato finale, allora

    un. Restituisce il record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.

  4. Se il primo è un surrogato finale o posizione + 1 = dimensione, allora

    a. Restituire il record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  5. Sia secondo l'unità di codice nella posizione di indice + 1 all'interno della stringa.

  6. Se il secondo non è un surrogato finale, allora

    un. Restituisce il record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.

  7. Impostare cp su! UTF16DecodeSurrogatePair (primo, secondo).

  8. Restituisce il record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.

Quindi, quando si scorre su una stringa con Array.from, restituisce CodeUnitCount di 2 solo quando il carattere in questione è l'inizio di una coppia surrogata. I personaggi che vengono interpretati come coppie surrogate sono descritti qui :

Tali operazioni applicano un trattamento speciale a ogni unità di codice con un valore numerico compreso tra 0xD800 e 0xDBFF (definito dallo standard Unicode come surrogato principale , o più formalmente come unità di codice surrogato elevato) e ogni unità di codice con un valore numerico nell'intervallo compreso tra 0xDC00 e 0xDFFF (definito come surrogato finale, o più formalmente come unità di codice surrogato basso) utilizzando le seguenti regole ..:

षि non è una coppia surrogata:

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

Ma 👍i personaggi sono:

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

Il primo codice carattere di '👍'è, in esadecimale, D83D, che si trova nel raggio di 0xD800 to 0xDBFFsurrogati principali. Al contrario, il primo codice carattere di 'षि'è molto più basso e non lo è. Quindi 'षि'viene diviso, ma '👍'non lo fa.

षिè composto da due caratteri separati: , Devanagari Lettera Ssa , e ि, Devanagari vocale I . Quando si trovano uno accanto all'altro in questo ordine, si combinano graficamente in un singolo personaggio visivamente, nonostante siano composti da due personaggi separati.

Al contrario, i codici dei caratteri hanno senso 👍 solo se uniti come un unico glifo. Se provi a usare una stringa con uno dei due punti di codice senza l'altro, otterrai un simbolo senza senso:

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


10
Penso che, sebbene per lo più corretta, utile e con citazioni attentamente fornite, questa risposta non spieghi chiaramente la differenza chiave tra i due casi: dal punto di vista Unicode, in षिrealtà sono due caratteri con punti di codice distinti combinati per formare un singolo glifo (un carattere astratto , come compreso dagli umani). Questo è in contrasto con l' 👍emoji, che è un personaggio completo in sé e per sé, anche se il suo punto di codice è abbastanza alto da dover essere diviso in una coppia surrogata. Credo che chiarire che potrebbe aiutare questa risposta (altrimenti preziosa) molto.
rinoceronte

In particolare, la consonante ष (ṣ) e la vocale ि (i) si combinano graficamente nella sillaba षि (ṣi)
Amadan,

@CertainPerformance C'è solo un punto di codice in "👍". Ciò suggerisce che la terminologia in questa risposta potrebbe essere errata.
Ben Aston,

13

UTF-16 (la codifica utilizzata per le stringhe in js) utilizza unità a 16 bit. Quindi ogni unicode che può essere rappresentato usando 15 bit è rappresentato come un punto di codice, tutto il resto come due, noto come coppie surrogate . L' iteratore di stringhe scorre su punti di codice.

UTF-16 su Wikipedia


8

Riguarda il codice dietro i personaggi. Alcuni sono codificati in due byte (UTF-16) e interpretati da Array.fromdue caratteri. Devo controllare l'elenco dei personaggi:

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)));


Per la funzione che visualizza il codice esadecimale:

Javascript: stringa Unicode in esadecimale

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.