В этой статье я хочу немного выйти за рамки традиционного представления о том, что «Строка — это последовательность символов». Мы посмотрим, что скрывается за этим мышлением, и попытаемся понять возможную реализацию строк в JavaScript.
Что такое строка ECMAScript?
Спецификация определяет тип String следующим образом:
Строковый тип — это набор всех упорядоченных последовательностей из нуля или более 16-битных целых чисел без знака («элементов») до максимальной длины 2⁵³–1 элемента.
Это довольно короткое, но очень объемное определение. Выделю только два момента:
- Последовательность (55358, 56736) имеет тип String.
- Спецификация отделяет внутреннее представление (16-битные целые числа без знака) от внешнего (фактические символы).
Итак, как же последовательность (55358, 56736) становится вездесущим символом 🦠?
Как интерпретируются строки ECMAScript?
Чтобы ответить на этот вопрос, нам нужно понять пару определений из кодировки UTF-16.
Как и спецификация ECMAScript, кодировка UTF-16 использует 16-битные целые числа без знака для кодирования символов. Целые числа, начинающиеся с 55296 (0xD800 в шестнадцатеричном формате) до 56319 (0xDBFF), называются «начальным суррогатом», а целые числа, начинающиеся с 56320 (0xDC00) до 57343 (0xDFFF), называются «конечным суррогатом». Полная пара (ведущий суррогат, замыкающий суррогат) называется «суррогатной парой».
Теперь мы можем понять нашу последовательность (55358, 56736) или (0xD83E, 0xDDA0) в шестнадцатеричном формате. Как мы видим, это суррогатная пара. Спецификация ECMAScript предлагает использовать следующую формулу для получения окончательного символа 🦠:
(c₁–0xD800) × 0x400 + (c₂–0xDC00) + 0x10000
где (c₁, c₂) = (начальный суррогат, замыкающий суррогат).
Подставляя наши значения, получаем:
(0xD83E — 0xD800) * 0x400 + (0xDDA0–0xDC00) + 0x10000 = 0x1F9A0.
Действительно, в таблице символов UTF-16 есть символ с таким же шестнадцатеричным кодом.
Как ECMAScript интерпретирует последовательность чисел
Теперь мы можем сформулировать упрощенный алгоритм, который можно использовать для преобразования последовательности 16-битных целых чисел без знака в последовательность реальных символов.
Я предполагаю, что константа charTable — это карта пар ‹целочисленный код, символ›, а константа sequence — это начальная последовательность 16-битных целых чисел без знака.
Я намеренно не обрабатывал искаженный ввод, чтобы сделать код простым. Все необходимые чеки вы можете выписать дома, если хотите 😉
Когда длина не так очевидна
Проделаем обратные операции и найдем суррогатную пару для символа 🦠.
Как мы видели, для кодирования символа требуется два целых числа. В ECMAScript это означает, что длина символа также равна двум. Мы можем использовать функцию codePointAt, чтобы получить фактическое целое число, используемое на внутреннем уровне.
Собираем все вместе
- Каждая строка в ECMAScript представляет собой упорядоченную последовательность 16-битных целых чисел без знака.
- Реализации ECMAScript используют алгоритм для преобразования этой последовательности в текстовое представление.
- Строка с одним символом может иметь разную длину
Домашнее задание
Что такое строка, внутреннее представление которой соответствует следующей последовательности: (1055, 1088, 1080, 1074, 1077, 1090)?