Array.from
спочатку намагається викликати ітератор аргументу, якщо він має його, а рядки мають ітератори, тому він викликає String.prototype[Symbol.iterator]
, так що давайте подивимось, як працює метод прототипу. Це описано в специфікації тут :
- Нехай О буде? RequireObjectCoercible (це значення).
- Нехай S бути? ToString (O).
- Повернути CreateStringIterator (S).
Дивлячись в CreateStringIterator
кінці кінців приведе вас 21.1.5.2.1 %StringIteratorPrototype%.next ( )
, що робить:
- Нехай cp буде! CodePointAt (s, позиція).
- Нехай resultString - це значення String, що містить cp. [[CodeUnitCount]] послідовних кодових одиниць, починаючи з початку з блоком коду в позиції індексу.
- Встановіть O [[StringNextIndex]] в положення + cp. [[CodeUnitCount]].
- Повернути CreateIterResultObject (resultString, false).
Це CodeUnitCount
те, що вас цікавить. Це число походить від CodePointAt :
- Дозвольте спочатку бути одиницею коду в позиції індексу в рядку.
- Нехай cp - кодова точка, числове значення якої є першою.
Якщо спочатку не є провідним сурогатом чи заднім сурогатом, то
а. Повернути запис { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }
.
Якщо спочатку є сурогат чи позиція + 1 = розмір, то
a. Поверніть Запис { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Нехай другим є одиниця коду в позиції індексу + 1 в рядку.
Якщо другий не є сурогатом, що значить,
а. Повернути запис { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Встановити cp на! UTF16DecodeSurrogatePair (перший, другий).
Повернути запис { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }
.
Отже, при ітерації над рядком з Array.from
він повертає CodeUnitCount 2 лише тоді, коли відповідний символ є початком сурогатної пари. Символи , які інтерпретуються як сурогатні пари описані тут :
Такі операції застосовують спеціальну обробку для кожної одиниці коду з числовим значенням в межах включеного діапазону від 0xD800 до 0xDBFF (визначений стандартом Unicode як провідний сурогат , або більш формально як одиниця коду з високим сурогатом) і до кожної одиниці коду з числовим значенням в діапазоні включення від 0xDC00 до 0xDFFF (визначається як задні сурогат, або формальніше як одиниця коду з низьким сурогатом) з використанням наступних правил ..:
षि
не є сурогатною парою:
console.log('षि'.charCodeAt()); // First character code: 2359, or 0x937
console.log('षि'.charCodeAt(1)); // Second character code: 2367, or 0x93F
Але 👍
символи:
console.log('👍'.charCodeAt()); // 55357, or 0xD83D
console.log('👍'.charCodeAt(1)); // 56397, or 0xDC4D
Першим символьним кодом '👍'
, у шістнадцятковій частині, є D83D, що знаходиться в межах 0xD800 to 0xDBFF
провідних сурогатів. На відміну від цього, перший код символу 'षि'
значно нижчий, і ні. Таким чином, 'षि'
розбивається на частини, але '👍'
це не так.
षि
складається з двох окремих символів: ष
, деванагарі Лист Ssa , і ि
, деванагарі Vowel Вхід I . Коли поруч один з одним в цьому порядку, вони візуально поєднуються в єдиний символ, незважаючи на те, що вони складаються з двох окремих символів.
Навпаки, символьні коди мають сенс 👍
лише тоді, коли вони разом, як єдиний гліф. Якщо ви спробуєте використати рядок з будь-якою кодовою точкою без іншої, ви отримаєте символ нісенітниці:
console.log('👍'[0]);
console.log('👍'[1]);