Array.from
primero intenta invocar el iterador del argumento si tiene uno, y las cadenas tienen iteradores, por lo que invoca String.prototype[Symbol.iterator]
, así que veamos cómo funciona el método prototipo. Se describe en la especificación aquí :
- Deja que O sea? RequireObjectCoercible (este valor).
- Seamos ? ToString (O).
- Devuelve CreateStringIterator (S).
Mirar hacia arriba CreateStringIterator
finalmente te lleva a lo 21.1.5.2.1 %StringIteratorPrototype%.next ( )
que hace:
- Deja que cp sea! CodePointAt (s, posición).
- Deje que resultString sea el valor de cadena que contiene cp. [[CodeUnitCount]] unidades de código consecutivas desde s que comienzan con la unidad de código en la posición de índice.
- Establezca O. [[StringNextIndex]] en la posición + cp. [[CodeUnitCount]].
- Devuelve CreateIterResultObject (resultString, false).
Esto CodeUnitCount
es lo que le interesa. Este número proviene de CodePointAt :
- Sea primero la unidad de código en la posición de índice dentro de la cadena.
- Deje que cp sea el punto de código cuyo valor numérico sea el primero.
Si primero no es un sustituto principal o sustituto final, entonces
a. Devuelve el registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }
.
Si primero es un sustituto o posición final + 1 = tamaño, entonces
a. Devuelva el registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Sea el segundo la unidad de código en la posición de índice + 1 dentro de la cadena.
Si el segundo no es un sustituto final, entonces
a. Devuelve el registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Establecer cp a! UTF16DecodeSurrogatePair (primero, segundo).
Devuelve el registro { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }
.
Entonces, al iterar sobre una cadena con Array.from
, devuelve un CodeUnitCount de 2 solo cuando el carácter en cuestión es el comienzo de un par sustituto. Los caracteres que se interpretan como pares sustitutos se describen aquí :
Dichas operaciones aplican un tratamiento especial a cada unidad de código con un valor numérico en el rango inclusivo 0xD800 a 0xDBFF (definido por el Estándar Unicode como un sustituto principal , o más formalmente como una unidad de código de alto sustituto) y cada unidad de código con un valor numérico en el rango inclusivo 0xDC00 a 0xDFFF (definido como un sustituto final, o más formalmente como una unidad de código de bajo sustituto) utilizando las siguientes reglas ..:
षि
no es un par sustituto:
console.log('षि'.charCodeAt()); // First character code: 2359, or 0x937
console.log('षि'.charCodeAt(1)); // Second character code: 2367, or 0x93F
Pero 👍
los personajes son:
console.log('👍'.charCodeAt()); // 55357, or 0xD83D
console.log('👍'.charCodeAt(1)); // 56397, or 0xDC4D
El primer código de carácter de '👍'
es, en hexadecimal, D83D, que está dentro del rango 0xD800 to 0xDBFF
de los sustitutos principales. Por el contrario, el primer código de caracteres 'षि'
es mucho más bajo y no lo es. Entonces 'षि'
se separa, pero '👍'
no lo hace.
षि
se compone de dos caracteres distintos: ष
, devanagari Carta Ssa , y ि
, devanagari vocal sesión I . Cuando están uno al lado del otro en este orden, se combinan gráficamente en un solo personaje visualmente, a pesar de estar compuestos por dos personajes separados.
En contraste, los códigos de caracteres 👍
solo tienen sentido cuando están juntos como un solo glifo. Si intenta usar una cadena con cualquier punto de código sin el otro, obtendrá un símbolo sin sentido:
console.log('👍'[0]);
console.log('👍'[1]);