Глубокое погружение в тему
Этот раздел более подробно описывает, как устроены строки. Такие знания пригодятся, если вы намерены работать с эмодзи, редкими математическими символами, иероглифами, либо с ещё какими-то редкими символами.
Если вы не планируете их поддерживать, эту секцию можно пропустить.
Суррогатные пары
Многие символы возможно записать одним 16-битным словом: это и буквы большинства европейских языков, и числа, и даже многие иероглифы.
Но 16 битов — это 65536 комбинаций, так что на все символы этого, разумеется, не хватит. Поэтому редкие символы записываются двумя 16-битными словами — это также называется «суррогатная пара».
Длина таких строк — 2
:
alert( '𝒳'.length ); // 2, MATHEMATICAL SCRIPT CAPITAL X
alert( '😂'.length ); // 2, FACE WITH TEARS OF JOY
alert( '𩷶'.length ); // 2, редкий китайский иероглиф
Обратите внимание, суррогатные пары не существовали, когда был создан JavaScript, поэтому язык не обрабатывает их адекватно!
Ведь в каждой из этих строк только один символ, а length
показывает длину 2
.
String.fromCodePoint
и str.codePointAt
— два редких метода, правильно работающие с суррогатными парами, но они и появились в языке недавно. До них были только String.fromCharCode и str.charCodeAt. Эти методы, вообще, делают то же самое, что fromCodePoint/codePointAt
, но не работают с суррогатными парами.
Получить символ, представленный суррогатной парой, может быть не так просто, потому что суррогатная пара интерпретируется как два символа:
alert( '𝒳'[0] ); // странные символы…
alert( '𝒳'[1] ); // …части суррогатной пары
Части суррогатной пары не имеют смысла сами по себе, так что вызовы alert
в этом примере покажут лишь мусор.
Технически, суррогатные пары возможно обнаружить по их кодам: если код символа находится в диапазоне 0xd800..0xdbff
, то это — первая часть суррогатной пары. Следующий символ — вторая часть — имеет код в диапазоне 0xdc00..0xdfff
. Эти два диапазона выделены исключительно для суррогатных пар по стандарту.
В данном случае:
// charCodeAt не поддерживает суррогатные пары, поэтому возвращает код для их частей
alert( '𝒳'.charCodeAt(0).toString(16) ); // d835, между 0xd800 и 0xdbff
alert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3, между 0xdc00 и 0xdfff
Дальше в главе Перебираемые объекты будут ещё способы работы с суррогатными парами. Для этого есть и специальные библиотеки, но нет достаточно широко известной, чтобы предложить её здесь.
Диакритические знаки и нормализация
Во многих языках есть символы, состоящие из некоторого основного символа со знаком сверху или снизу.
Например, буква a
— это основа для àáâäãåā
. Наиболее используемые составные символы имеют свой собственный код в таблице UTF-16. Но не все, в силу большого количества комбинаций.
Чтобы поддерживать любые комбинации, UTF-16 позволяет использовать несколько юникодных символов: основной и дальше один или несколько особых символов-знаков.
Например, если после S
добавить специальный символ «точка сверху» (код \u0307
), отобразится Ṡ.
alert( 'S\u0307' ); // Ṡ
Если надо добавить сверху (или снизу) ещё один знак — без проблем, просто добавляем соответствующий символ.
Например, если добавить символ «точка снизу» (код \u0323
), отобразится S с точками сверху и снизу: Ṩ
.
Добавляем два символа:
alert( 'S\u0307\u0323' ); // Ṩ
Это даёт большую гибкость, но из-за того, что порядок дополнительных символов может быть различным, мы получаем проблему сравнения символов: можно представить по-разному символы, которые ничем визуально не отличаются.
Например:
let s1 = 'S\u0307\u0323'; // Ṩ, S + точка сверху + точка снизу
let s2 = 'S\u0323\u0307'; // Ṩ, S + точка снизу + точка сверху
alert( `s1: ${s1}, s2: ${s2}` );
alert( s1 == s2 ); // false, хотя на вид символы одинаковы (?!)
Для решения этой проблемы есть алгоритм «юникодной нормализации», приводящий каждую строку к единому «нормальному» виду.
Его реализует метод str.normalize().
alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true
Забавно, но в нашем случае normalize()
«схлопывает» последовательность из трёх символов в один: \u1e68
— S с двумя точками.
alert( "S\u0307\u0323".normalize().length ); // 1
alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true
Разумеется, так происходит не всегда. Просто Ṩ — это достаточно часто используемый символ, поэтому создатели UTF-16 включили его в основную таблицу и присвоили ему код.
Подробнее о правилах нормализации и составлении символов можно прочитать в дополнении к стандарту Юникод: Unicode Normalization Forms. Для большинства практических целей информации из этого раздела достаточно.
Итого
- Есть три типа кавычек. Строки, использующие обратные кавычки, могут занимать более одной строки в коде и включать выражения
${…}
. - Строки в JavaScript кодируются в UTF-16.
- Есть специальные символы, такие как
\n
, и можно добавить символ по его юникодному коду, используя\u…
. - Для получения символа используйте
[]
. - Для получения подстроки используйте
slice
илиsubstring
. - Для того, чтобы перевести строку в нижний или верхний регистр, используйте
toLowerCase/toUpperCase
. - Для поиска подстроки используйте
indexOf
илиincludes/startsWith/endsWith
, когда надо только проверить, есть ли вхождение. - Чтобы сравнить строки с учётом правил языка, используйте
localeCompare
.
Строки также имеют ещё кое-какие полезные методы:
str.trim()
— убирает пробелы в начале и конце строки.str.repeat(n)
— повторяет строкуn
раз.- …и другие, которые вы можете найти в справочнике.