Array.from
primeiro tenta invocar o iterador do argumento, se ele tiver um, e as strings possuem iteradores, portanto, ele invoca String.prototype[Symbol.iterator]
, então vamos ver como o método prototype funciona. Está descrito na especificação aqui :
- Seja O seja? RequireObjectCoercible (este valor).
- Vamos ser ? ToString (O).
- Retorne CreateStringIterator (S).
Olhar para cima CreateStringIterator
leva você a 21.1.5.2.1 %StringIteratorPrototype%.next ( )
, o que faz:
- Seja cp! CodePointAt (s, posição).
- Seja resultString o valor da String que contém cp. [[CodeUnitCount]] unidades de código consecutivas desde s começando com a unidade de código na posição do índice.
- Defina O. [[StringNextIndex]] para a posição + cp. [[CodeUnitCount]].
- Retorno CreateIterResultObject (resultString, false).
É nissoCodeUnitCount
que você está interessado. Esse número vem do CodePointAt :
- Seja primeiro a unidade de código na posição do índice dentro da string.
- Seja cp o ponto do código cujo valor numérico é o primeiro.
Se primeiro não for um substituto líder ou um substituto final, então
uma. Retorne o registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }
.
Se primeiro for um substituto à direita ou uma posição + 1 = tamanho, então
a.Retorne o registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Em segundo lugar, seja a unidade de código na posição de índice + 1 dentro da string.
Se o segundo não for um substituto final, então
uma. Retorne o registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Defina cp como! UTF16DecodeSurrogatePair (primeiro, segundo).
Retorne o registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }
.
Portanto, ao iterar sobre uma string com Array.from
, ele retornará um CodeUnitCount de 2 somente quando o caractere em questão for o início de um par substituto. Caracteres que são interpretados como pares substitutos são descritos aqui :
Tais operações aplicam tratamento especial a todas as unidades de código com um valor numérico no intervalo inclusivo de 0xD800 a 0xDBFF (definido pelo Padrão Unicode como um substituto principal ou mais formalmente como um código de alto substituto) e todas as unidades de código com um valor numérico no intervalo inclusivo de 0xDC00 a 0xDFFF (definido como um substituto à direita ou mais formalmente como uma unidade de código com um substituto baixo) usando as seguintes regras ..:
षि
não é um par substituto:
console.log('षि'.charCodeAt()); // First character code: 2359, or 0x937
console.log('षि'.charCodeAt(1)); // Second character code: 2367, or 0x93F
Mas 👍
os personagens são:
console.log('👍'.charCodeAt()); // 55357, or 0xD83D
console.log('👍'.charCodeAt(1)); // 56397, or 0xDC4D
O primeiro código de caractere de '👍'
é, em hexadecimal, D83D, que está dentro do intervalo 0xD800 to 0xDBFF
dos principais substitutos. Por outro lado, o primeiro código de caractere de 'षि'
é muito menor e não é. Então, isso 'षि'
se divide, mas '👍'
não acontece.
षि
é composto de dois caracteres separados: ष
, Devanagari Carta Ssa , e ि
, Devanagari vogal Sinal I . Quando próximos um do outro nesta ordem, eles são combinados graficamente em um único caractere visualmente, apesar de serem compostos por dois caracteres separados.
Por outro lado, os códigos de caracteres 👍
só fazem sentido quando juntos como um único glifo. Se você tentar usar uma string com um ponto de código sem o outro, receberá um símbolo sem sentido:
console.log('👍'[0]);
console.log('👍'[1]);