Array.from
Сначала попытается вызвать итератор аргумента, если он есть, и строки имеют итераторы, поэтому он вызывает String.prototype[Symbol.iterator]
, поэтому давайте посмотрим, как работает метод-прототип. Это описано в спецификации здесь :
- Позволь О быть? RequireObjectCoercible (это значение).
- Давай будем ? ToString (О).
- Вернуть CreateStringIterator (S).
Взгляд в CreateStringIterator
конечном итоге приведет вас к 21.1.5.2.1 %StringIteratorPrototype%.next ( )
:
- Пусть cp будет! CodePointAt (s, позиция).
- Пусть resultString будет значением String, содержащим cp. [[CodeUnitCount]] последовательные кодовые единицы от s, начиная с кодовой единицы в позиции индекса.
- Установите O. [[StringNextIndex]] в положение + cp. [[CodeUnitCount]].
- Вернуть CreateIterResultObject (resultString, false).
Это CodeUnitCount
то, что вас интересует. Этот номер взят из CodePointAt :
- Пусть first будет единица кода в позиции индекса в строке.
- Пусть cp будет кодовой точкой, числовое значение которой равно значению first.
Если первый не является ведущим суррогатом или последним суррогатом, то
а. Верните запись { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }
.
Если первым является конечный суррогат или позиция + 1 = размер, то
Возврат записи { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Пусть second будет единица кода в позиции индекса + 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]);