Допустим, мы хотим написать функцию, которая превращает строку в массив символов. Попробуем написать это:

Простой. И что отображает этот console.log?

[ 'R', 'o', 'b', 'i', 'n', ' ', 'H', 'o', 'o', 'd' ]

Хороший! Но поддерживает ли он Unicode? Давайте попробуем строку с символами иврита:

А также…

[ 'R', 'o', 'b', 'i', 'n', ' ', 'H', 'o', 'ו', 'ו', 'd' ]

Да! Я могу перебирать символы в Юникоде. Конец истории, все работает! Я так счастлив, я хочу попробовать это со счастливым смайликом:

А также…

[ 'R', 'o', 'b', 'i', 'n', ' ', 'H', '�', '�', '�', '�', 'd' ]

Ой. Нет. Почему я не вижу смайликов? И почему он разделил эти смайлы на четыре символа, а не на два?

Чтобы понять это, вернемся к истокам времен ...

Дни до Unicode

Когда-то был ASCII. ASCII - это кодировка, которая определяет число (называемое кодовой точкой) для каждой буквы латинского алфавита, а также включает числа, множество знаков препинания и некоторые управляющие символы, такие как CR (13) и LF. (10).

И это было хорошо давно. Но через некоторое время европейцы подняли там античную голову и захотели всех этих персонажей с акцентами на них, например…. Но в таблице ASCII места больше не было - ASCII - это 7-битная кодировка, а это значит, что она может обрабатывать только кодовые точки от 0 до 127.

Но если мы создадим 8-битную кодировку? У нас будет еще 128 кодовых точек. Это достаточно? Да! Так родилась Latin-1, или, как ее сейчас называют, ISO-8859–1.

Но потом появились все эти надоедливые языки. Например, иврит. Есть ли место для иврита и латинских букв? Неа. Так родился ISO-8859–8, содержащий ASCII плюс все символы иврита.

Но совместимы ли они? Нет. При кодировании символов в байты (то есть в кодовые точки) вы должны выяснить, хотите ли вы кодировать латинские символы или символы иврита. У вас не может быть обоих, потому что одна и та же кодовая точка имеет разные символы, сопоставленные с ней в двух кодировках.

Первые дни Unicode

Юникод «решил» эту проблему. Люди во всем мире понимали, что такая ситуация не может продолжаться. И хотя «Мир во всем мире» не является решенной проблемой, стандартизация того, какой код сопоставляется каким символам , является решенной проблемой. Он называется Unicode.

Unicode определяет длинную длинную длинную таблицу, в которой числа (кодовые точки) отображаются в символы.

А каков диапазон кодовых точек? Что ж, они определили его как 32-битное (4 ГБ символов), но они заметили, что почти все языки на Земле могут уместиться в 16-битном (то есть 64K символов). Были некоторые упрямые (и мертвые) языки, которые не подходили и нуждались в более чем 16-битном коде, но кого это волновало? Только некоторые ученые.

Поэтому, когда появился новый язык - Java - было решено, что все строки на этом языке будут кодировать символы в 16-битном формате, то есть иметь ширину 2 байта. Эта кодировка называется UTF-16. Это решение было скопировано на других языках, таких как C #. Поддерживал ли UTF-16 кодовые точки выше 65 535, то есть выше 16 бит? Да, но для этого требовалось четыре байта, то есть два «символа». Это означает, что если строка содержит кодовую точку, превышающую 65 535, она будет закодирована как два «символа» в строке.

И угадайте, какой другой язык скопировал это поведение? Правильно - JavaScript.

Вернуться к примеру

Так почему еврейские символы в

stringToArray('Robin Hoווd')

Сгенерируйте нужных персонажей, и все же это:

stringToArray('Robin H😀😀d')

нет?

Потому что иврит находится в 16-битной «плоскости», но все символы Emoji находятся над ними.

Да, эмодзи появились с опозданием, и ничего не оставалось, кроме как добавить их над 16-битной плоскостью.

Внезапно не только ученые стали интересоваться символами Unicode выше 16-битного места. Все теперь заботятся об этих персонажах. Внезапно все сталкиваются с проблемами, связанными с этими персонажами.

Но, как мы видели выше, ES5 плохо справляется с этими персонажами.

Итак, как мы перебираем символы Юникода?

В ES5 это на самом деле довольно сложно, и в основном это поиск этих суррогатных пар и работа с ними. Вы можете найти решение здесь.

Но ES6 пришлось решить эту проблему, иначе Emoji не работали бы, а все любят Emojis! Как они ее решили? Они определили, что строка является итерируемой, и что итератор по строке будет иметь дело с суррогатными парами автоматически.

Посмотрим, правильно ли это:

Итак, мы создаем итератор из итерируемой строки, используя str[Symbol.iterator](). Затем мы перебираем итератор, используя протокол итератора в JavaScript: iterator.next().

И да, это работает. Он отображает 😀 эмодзи!

Использование for-of

Но есть более простой способ перебора итерации - for-of:

И это работает!

[ 'R', 'o', 'b', 'i', 'n', ' ', 'H', '😀', '😀', 'd' ]

И есть еще более простое решение:

Array.from

И это работает - Array.from генерирует массив из строки - правильный массив. Здесь мы сопоставляем 😀 с 🙃 и получаем:

[ 'R', 'o', 'b', 'i', 'n', ' ', 'H', '🙃', '🙃', 'd' ]

Мы даже можем превратить его обратно в строку, используя string.join:

И получить…

Robin H🙃🙃d

Миссия выполнена.

Пролог - правильно ли это передает Вавилон?

Я не знал, может ли babel транспиляция правильно обрабатывать символы Unicode.

Я проверил (можно попробовать по ссылке), все работает! Так что, если вы используете Babel, вы можете использовать этот метод, и он все равно будет работать.