Array.from
сначала пытается вызвать итератор аргумента, если он есть, и в строках есть итераторы, поэтому он вызывает String.prototype[Symbol.iterator]
, поэтому давайте посмотрим, как работает метод-прототип. Это описано в спецификации здесь :
- Пусть O будет? RequireObjectCoercible (это значение).
- Пусть S будет? ToString (O).
- Возврат CreateStringIterator (S).
Поиск CreateStringIterator
в конечном итоге приведет вас к 21.1.5.2.1 %StringIteratorPrototype%.next ( )
, что делает :
Пусть cp будет! CodePointAt (s, position). Пусть resultString будет строковым значением, содержащим cp. [[CodeUnitCount]] последовательные кодовые единицы от s, начиная с кодовой единицы в позиции индекса. Set O. [[StringNextIndex]] в положение + cp. [[CodeUnitCount]]. Возврат CreateIterResultObject (resultString, false).
CodeUnitCount
- это то, что вы ' Вы заинтересованы в этом. Этот номер прибывает из CodePointAt :
Пусть first будет кодовой единицей в позиции индекса в строке. Пусть cp будет кодовой точкой, чье значение numberri c соответствует значению first.
Если first is не ведущий суррогат или отставший суррогат, тогда
a. Вернуть запись { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }
.
Если первым является конечный суррогат или позиция + 1 = размер, то
a. Возврат записи { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Пусть секунда - это кодовая единица в позиции индекса + 1 в строке.
Если секунда не является последним суррогатом, то
а. Верните запись { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }
.
Установите для cp значение! UTF16DecodeSurrogatePair (first, second).
Вернуть запись { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }
.
Итак, при итерации по строке с Array.from
, он возвращает CodeUnitCount, равный 2, только когда рассматриваемый символ является началом суррогатной пары. Символы, которые интерпретируются как суррогатные пары, описаны здесь :
Такие операции применяют особый режим к каждой единице кода со значением цифры c в включенном диапазоне 0xD800 0xDBFF (определяется стандартом Unicode как ведущий суррогат или, более формально, как высокосуррогатная кодовая единица) и каждая кодовая единица со значением цифры c в включающем диапазоне от 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 и ि
, Деванагари гласный знак I . Находясь рядом друг с другом в этом порядке, они визуально графически объединяются в один символ, несмотря на то, что состоят из двух отдельных символов.
В отличие от кодов символов ?
только имеет смысл, когда вместе, как один глиф. Если вы попытаетесь использовать строку с одной кодовой точкой без другой, вы получите бессмысленный символ:
console.log('?'[0]);
console.log('?'[1]);