У JavaScript текстові дані зберігаються у вигляді рядків. Не існує окремого типу для одного символу.
Внутрішній формат для рядків завжди UTF-16, він не привʼязаний до кодування сторінки.
Лапки
Згадаймо види лапок.
Рядки можуть бути включені в одинарні лапки, подвійні лапки або зворотні знаки:
let single = 'одинарні-лапки';
let double = "подвійні-лапки";
let backticks = `зворотні-лапки`;
Одинарні та подвійні лапки по суті однакові. Однак зворотні лапки дозволяють нам вставляти будь-який вираз у рядок, загортаючи його у ${…}:
Ще однією перевагою використання зворотних лапок є те, що вони дозволяють рядку охоплювати кілька ліній:
Виглядає природно, правда? Але одинарні або подвійні лапки так не працюють.
Якщо ми спробуємо їх використати в кілька рядків, буде помилка:
Одинарні та подвійні лапки беруть свій початок з давніх часів створення мови, коли не було потреби у багатолінійних рядках. Зворотні лапки зʼявилися набагато пізніше і тому є більш універсальними.
Зворотні лапки також дозволяють нам задати “шаблонну функцію” перед першими зворотніми лапками. Синтаксис такий: func`string`. Функція func викликається автоматично, отримує рядок і вбудовані в неї вирази і може їх обробити. Це називається “теговим шаблоном”, це рідко використовується на практиці, але ви можете прочитати детальніше про це на MDN: Template literals.
Спеціальні символи
Ще можна створити багатолінійні рядки з одинарними та подвійними лапками за допомогою так званого “символу нового рядка”, записаного як \n, який позначає розрив рядка:
Наприклад, ці два рядки рівнозначні, просто написані по-різному:
Є й інші, менш поширені “спеціальні” символи.
| Символ | Опис |
|---|---|
\n |
Розрив рядка |
\r |
У текстових файлах Windows комбінація двох символів \r\n являє собою розрив рядка, тоді як в інших ОС, це просто \n. Так склалось з історичних причин, більшість ПЗ під Windows також розуміє \n |
\', \", \` |
Лапки |
\\ |
Зворотний слеш |
\t |
Знак табуляції |
\b, \f, \v |
Backspace, Form Feed, Vertical Tab – зберігаються для зворотної сумісності, зараз не використовуються |
Усі спеціальні символи починаються зі зворотного слеша \. Його також називають “символом екранування”.
Оскільки він не зовсім звичайний, то якщо нам потрібно показати зворотний слеш \ у рядку, нам потрібно подвоїти його:
Так звані “екрановані” лапки \', \", \` використовуються для вставки цих лапок в рядок, який обмежено таким же типом лапок.
Наприклад:
Як бачите, ми повинні “екранувати” лапку зворотним слешем \', оскільки інакше ця лапка означала б кінець рядка.
Звісно ж, потрібно “екранувати” лише такі лапки, якими обрамлений рядок. Або ще більш елегантним рішенням було б замість цього скористатися подвійними або зворотними лапками:
Окрім цих спеціальних символів, існує також спеціальна нотація для кодів Unicode \u…, вона використовується рідко та описана в додатковому розділі про Unicode.
Довжина рядка
Властивість length містить в собі довжину рядка:
Зверніть увагу, що \n – це один спеціальний символ, тому довжина рівна 4.
length – це властивістьЛюди з досвідом роботи в інших мовах випадково намагаються викликати властивість, додаючи круглі дужки: вони пишуть str.length() замість str.length. Це не спрацює.
Зверніть увагу, що str.length – це числове значення, а не функція, додавати дужки не потрібно. Не .length(), а .length.
Доступ до символів
Отримати символ, котрий займає позицію pos, можна за допомогою квадратних дужок: [pos], або викликати метод str.at(pos). Перший символ займає нульову позицію:
Як бачите, перевага методу .at(pos) полягає в тому, що він допускає від’ємну позицію. Якщо pos від’ємне число, тоді позиція відраховується з кінця рядка.
Отже, .at(-1) означає останній символ, а .at(-2) – передостанній, тощо.
Квадратні дужки завжди повертають undefined для від’ємних індексів, наприклад:
Ми також можемо перебрати рядок посимвольно, використовуючи for..of:
Рядки незмінні
В JavaScript рядки не можна змінювати. Змінити символ неможливо.
Спробуємо показати на прикладі:
Можна створити новий рядок замість старого, записавши його в ту саму змінну.
Ось так:
В наступних розділах ми побачимо більше прикладів.
Зміна регістру
Методи toLowerCase() та toUpperCase() змінюють регістр символів:
Або якщо ми хочемо перенести в нижній регістр конкретний символ:
Пошук підрядка
Існує декілька способів для пошуку підрядка.
str.indexOf
Перший метод – str.indexOf(substr, pos).
Він шукає підрядок substr в рядку str, починаючи з позиції pos, і повертає позицію, де знаходиться збіг, або якщо збігів не було знайдено, то -1.
Наприклад:
let str = 'Віджет з ідентифікатором';
alert( str.indexOf('Віджет') ); // 0, тому що 'Віджет' було знайдено на початку
alert( str.indexOf('віджет') ); // -1, збігів не знайдено, пошук чутливий до регістру
alert( str.indexOf("ід") ); // 1, підрядок "ід" знайдено на позиції 1 (..іджет з ідентифікатором)
Необовʼязковий другий параметр pos дозволяє нам почати пошук із заданої позиції.
Наприклад, перший збіг "ід" знаходиться на позиції 1. Щоб знайти наступний збіг, почнемо пошук з позиції 2:
Щоб знайти усі збіги, нам потрібно запустити indexOf в циклі. Кожен новий виклик починається з позиції після попереднього збігу:
Той самий алгоритм можна записати коротше:
str.lastIndexOf(substr, position)Також є схожий метод str.lastIndexOf(substr, position), що виконує пошук від кінця рядка до його початку.
У ньому будуть перераховані збіги в зворотному порядку.
Існує незручність з indexOf в умові if. Ми не можемо помістити його в if таким чином:
В прикладі вище alert не відображається, оскільки str.indexOf("Віджет") повертає 0 (це означає, що він знайшов збіг у початковій позиції). Це правильно, але if вважає, що 0 – це false.
Тому нам потрібно робити перевірку на -1, як тут:
includes, startsWith, endsWith
Сучасніший метод str.includes(substr, pos) повертає true/false в залежності від того чи є substr в рядку str.
Цей метод доцільно використовувати, коли потрібно перевірити чи є збіг, але не потрібна позиція:
Необовʼязковий другий аргумент pos – це позиція з якої почнеться пошук:
Відповідно, методи str.startsWith та str.endsWith перевіряють, чи починається і чи закінчується рядок певним підрядком.
Отримання підрядка
В JavaScript є 3 метода для отримання підрядка: substring, substr та slice.
str.slice(start [, end])-
Повертає частину рядка починаючи від
startдо (але не включно)end.Наприклад:
Якщо другий аргумент відсутній, тоді
sliceповерне символи до кінця рядка:Також для
start/endможна задати відʼємне значення. Це означає, що позиція буде рахуватися з кінця рядка: str.substring(start [, end])-
Повертає частину рядка між
startтаend(не включаючиend).Цей метод майже такий самий як і
slice, алеsubstringдозволяє задатиstartбільше значення, ніжend(у такому випадку він просто поміняє значенняstartіendмісцями).Наприклад:
Відʼємні аргументи (на відміну від slice) не підтримуються, вони інтерпретуються як
0. str.substr(start [, length])-
Повертає частину рядка з позиції
start, із заданою довжиноюlength.На відміну від попередніх методів, цей дозволяє вказати довжину
lengthзамість кінцевої позиції:Перший аргумент може бути відʼємним, щоб рахувати з кінця:
Цей метод міститься в Annex B специфікації мови. Це означає, що його мають підтримувати лише браузерні рушії Javascript, і не рекомендується його використовувати. На практиці це підтримується всюди.
Давайте підсумуємо ці методи щоб не заплутатись:
| Метод | вибирає… | відʼємні значення |
|---|---|---|
slice(start, end) |
від start до end (не включаючи end) |
дозволяє відʼємні значення |
substring(start, end) |
між start та end (не включаючи end) |
відʼємні значення інтерпретуються як 0 |
substr(start, length) |
length символів від start |
дозволяє відʼємні значення start |
Усі вони можуть виконати задачу. Формально substr має незначний недолік: він описаний не в основній специфікації JavaScript, а в Annex B, яка охоплює лише функції браузера, які існують переважно з історичних причин. Тому не браузерні середовища, можуть не підтримувати його. Але на практиці це працює всюди.
З двох інших варіантів slice дещо гнучкіший, він допускає від’ємні аргументи та коротший в записі.
Отже, достатньо запамʼятати лише slice з цих трьох методів.
Порівняння рядків
Як ми знаємо з розділу Оператори порівняння, рядки порівнюються символ за символом в алфавітному порядку.
Хоча, є деякі дивацтва.
-
Літера в малому регістрі завжди більша за літеру у великому:
-
Літери з діакритичними знаками “не по порядку”:
Це може призвести до дивних результатів, якщо ми відсортуємо ці назви країн. Зазвичай люди очікують, що
Zealandбуде післяÖsterreich.
Щоб розуміти, що відбувається, нам слід бути в курсі, що рядки в JavaScript реалізовані з використанням UTF-16. Тобто, кожен символ має свій відповідний числовий код.
Існують спеціальні методи, які дозволяють отримати символ по коду і навпаки.
str.codePointAt(pos)-
Повертає десяткове число, що є кодом символу на позиції
pos: String.fromCodePoint(code)-
Створює символ за його кодом
code
Тепер давайте подивимося на символи з кодами 65..220 (латинський алфавіт і трохи більше), створивши з них рядок:
Бачите? Спочатку виводяться великі символи, потім кілька спеціальних, потім символи нижнього регістру та Ö ближче до кінця виводу.
Тепер стає очевидним, чому a > Z.
Символи порівнюються за їх числовим кодом. Більший код означає, що символ більше. Код для a (97) більший за код для Z (90).
- Усі малі літери йдуть після великих, оскільки їхні коди більші.
- Деякі літери, як-от
Ö, стоять окремо від основного алфавіту. Тут його код більший за будь-що відaдоz.
Правильне порівняння
«Правильний» алгоритм порівняння рядків є складнішим, ніж може здатися, тому що для різних мов – різні алфавіти.
Отже, браузеру потрібно знати алфавіт якої мови використовувати для порівняння.
На щастя, усі сучасні браузери підтримують стандарт інтернаціоналізації ECMA-402.
Він забезпечує спеціальний метод для порівняння рядків з різних мов, дотримуючись їхніх правил.
Виклик str.localeCompare(str2) повертає ціле число, яке вказує, чи є str меншим, рівним чи більшим за str2 відповідно до правил мови:
- Повертає відʼємне число, якщо
strменше, ніжstr2. - Повертає додатне число, якщо
strбільше, ніжstr2. - Повертає
0, якщо вони рівні.
Наприклад:
Цей метод насправді має два додаткові аргументи, зазначені в документації, що дозволяє явно вказати йому мову (яку він типово визначає опираючись на середовище. І від чого залежить порядок букв), і встановити додаткові правила, як-от чутливість до регістру або чи слід розглядати різницю між "a" та "á".
Підсумки
- Є 3 види лапок. Зворотні лапки дозволяють рядку охоплювати кілька ліній і застосовувати вбудовувані вирази
${…}. - Ми можемо використовувати спеціальні символи, такі як розрив рядка
\n. - Щоб отримати символ, використовуйте:
[]або методat. - Щоб отримати підрядок, використовуйте:
sliceабоsubstring. - Щоб перевести рядок у нижній/верхній регістри, використовуйте:
toLowerCase/toUpperCase. - Щоб знайти підрядок, використовуйте:
indexOf, абоincludes/startsWith/endsWithдля простих перевірок. - Щоб порівняти рядки з урахуванням правил мови, використовуйте
localeCompare, інакше рядки порівнюються за кодами символів.
Є кілька інших корисних методів у рядках:
str.trim()– видаляє (“обрізає”) пробіли з початку та кінця рядка.str.repeat(n)– повторює рядокnразів.- …та багато іншого можна знайти в посібнику.
Рядки також мають методи пошуку/заміни регулярними виразами. Але це велика тема, тому пояснюється в окремому розділі Регулярні вирази.
Крім того, на даний момент важливо знати, що рядки базуються на кодуванні Unicode, і тому виникають проблеми з порівнянням, які ми описали вище. Більше про Unicode у розділі Юнікод, внутрішня будова рядків.