# Строки В JavaScript любые текстовые данные являются строками. Не существует отдельного типа "символ", который есть в ряде других языков. Внутренним форматом строк, вне зависимости от кодировки страницы, является [Юникод (Unicode)](http://ru.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4). [cut] ## Создание строк Строки создаются при помощи двойных или одинарных кавычек: ```js var text = "моя строка"; var anotherText = 'еще строка'; var str = "012345"; ``` В JavaScript нет разницы между двойными и одинарными кавычками. ### Специальные символы Строки могут содержать специальные символы. Самый часто используемый из таких символов -- это "перевод строки". Он обозначается как `\n`, например: ```js //+ run alert( 'Привет\nМир' ); // выведет "Мир" на новой строке ``` Есть и более редкие символы, вот их список:
Специальные символы
СимволОписание
\bBackspace
\fForm feed
\nNew line
\rCarriage return
\tTab
\uNNNNСимвол в кодировке Юникод с шестнадцатеричным кодом `NNNN`. Например, `\u00A9` -- юникодное представление символа копирайт ©
### Экранирование специальных символов Если строка в одинарных кавычках, то внутренние одинарные кавычки внутри должны быть *экранированы*, то есть снабжены обратным слешем `\'`, вот так: ```js var str = '*!*I\'m*/!* a JavaScript programmer'; ``` В двойных кавычках -- экранируются внутренние двойные: ```js //+ run var str = "I'm a JavaScript \"programmer\" "; alert( str ); // I'm a JavaScript "programmer" ``` Экранирование служит исключительно для правильного восприятия строки JavaScript. В памяти строка будет содержать сам символ без `'\'`. Вы можете увидеть это, запустив пример выше. Сам символ обратного слэша `'\'` является служебным, поэтому всегда экранируется, т.е пишется как `\\`: ```js //+ run var str = ' символ \\ '; alert( str ); // символ \ ``` Заэкранировать можно любой символ. Если он не специальный, то ничего не произойдёт: ```js //+ run alert( "\a" ); // a // идентично alert( "a" ); ``` ## Методы и свойства Здесь мы рассмотрим методы и свойства строк, с некоторыми из которых мы знакомились ранее, в главе [](/properties-and-methods). ### Длина length Одно из самых частых действий со строкой -- это получение ее длины: ```js //+ run var str = "My\n"; // 3 символа. Третий - перевод строки alert( str.length ); // 3 ``` ### Доступ к символам Чтобы получить символ, используйте вызов `charAt(позиция)`. Первый символ имеет позицию `0`: ```js //+ run var str = "jQuery"; alert( str.charAt(0) ); // "j" ``` В JavaScript **нет отдельного типа "символ"**, так что `charAt` возвращает строку, состоящую из выбранного символа. Также для доступа к символу можно также использовать квадратные скобки: ```js //+ run var str = "Я - современный браузер!"; alert( str[0] ); // "Я" ``` Разница между этим способом и `charAt` заключается в том, что если символа нет -- `charAt` выдает пустую строку, а скобки -- `undefined`: ```js //+ run alert( "".charAt(0) ); // пустая строка alert( "" [0] ); // undefined ``` Вообще же метод `charAt` существует по историческим причинам, ведь квадратные скобки -- проще и короче. [warn header="Вызов метода -- всегда со скобками"] Обратите внимание, `str.length` -- это *свойство* строки, а `str.charAt(pos)` -- *метод*, т.е. функция. Обращение к методу всегда идет со скобками, а к свойству -- без скобок. [/warn] ### Изменения строк Строки в JavaScript нельзя изменять. Можно прочитать символ, но нельзя заменить его. Как только строка создана -- она такая навсегда. Чтобы это обойти, создаётся новая строка и присваивается в переменную вместо старой: ```js //+ run var str = "строка"; str = str[3] + str[4] + str[5]; alert( str ); // ока ``` ### Смена регистра Методы `toLowerCase()` и `toUpperCase()` меняют регистр строки на нижний/верхний: ```js //+ run alert( "Интерфейс".toUpperCase() ); // ИНТЕРФЕЙС ``` Пример ниже получает первый символ и приводит его к нижнему регистру: ```js alert( "Интерфейс" [0].toLowerCase() ); // 'и' ``` ### Поиск подстроки Для поиска подстроки есть метод indexOf(подстрока[, начальная_позиция]). Он возвращает позицию, на которой находится `подстрока` или `-1`, если ничего не найдено. Например: ```js //+ run var str = "Widget with id"; alert( str.indexOf("Widget") ); // 0, т.к. "Widget" найден прямо в начале str alert( str.indexOf("id") ); // 1, т.к. "id" найден, начиная с позиции 1 alert( str.indexOf("widget") ); // -1, не найдено, так как поиск учитывает регистр ``` Необязательный второй аргумент позволяет искать, начиная с указанной позиции. Например, первый раз `"id"` появляется на позиции `1`. Чтобы найти его следующее появление -- запустим поиск с позиции `2`: ```js //+ run var str = "Widget with id"; alert(str.indexOf("id", 2)) // 12, поиск начат с позиции 2 ``` Также существует аналогичный метод lastIndexOf, который ищет не с начала, а с конца строки. [smart] Для красивого вызова `indexOf` применяется побитовый оператор НЕ `'~'`. Дело в том, что вызов `~n` эквивалентен выражению `-(n+1)`, например: ```js //+ run alert( ~2 ); // -(2+1) = -3 alert( ~1 ); // -(1+1) = -2 alert( ~0 ); // -(0+1) = -1 *!* alert( ~-1 ); // -(-1+1) = 0 */!* ``` Как видно, `~n` -- ноль только в случае, когда `n == -1`. То есть, проверка `if ( ~str.indexOf(...) )` означает, что результат `indexOf` отличен от `-1, т.е. совпадение есть. Вот так: ```js //+ run var str = "Widget"; if (~str.indexOf("get")) { alert( 'совпадение есть!' ); } ``` Вообще, использовать возможности языка неочевидным образом не рекомендуется, поскольку ухудшает читаемость кода. Однако, в данном случае, все в порядке. Просто запомните: `'~'` читается как "не минус один", а `"if ~str.indexOf"` читается как `"если найдено"`. [/smart] ### Поиск всех вхождений Чтобы найти все вхождения подстроки, нужно запустить `indexOf` в цикле. Как только получаем очередную позицию -- начинаем следующий поиск со следующей. Пример такого цикла: ```js //+ run var str = "Ослик Иа-Иа посмотрел на виадук"; // ищем в этой строке var target = "Иа"; // цель поиска var pos = 0; while (true) { var foundPos = str.indexOf(target, pos); if (foundPos == -1) break; alert( foundPos ); // нашли на этой позиции pos = foundPos + 1; // продолжить поиск со следующей } ``` Такой цикл начинает поиск с позиции `0`, затем найдя подстроку на позиции `foundPos`, следующий поиск продолжит с позиции `pos = foundPos+1`, и так далее, пока что-то находит. Впрочем, тот же алгоритм можно записать и короче: ```js //+ run var str = "Ослик Иа-Иа посмотрел на виадук"; // ищем в этой строке var target = "Иа"; // цель поиска *!* var pos = -1; while ((pos = str.indexOf(target, pos + 1)) != -1) { alert( pos ); } */!* ``` ### Взятие подстроки: substr, substring, slice. В JavaScript существуют целых 3 (!) метода для взятия подстроки, с небольшими отличиями между ними.
`substring(start [, end])`
Метод `substring(start, end)` возвращает подстроку с позиции `start` до, но не включая `end`. ```js //+ run var str = "*!*s*/!*tringify"; alert(str.substring(0,1)); // "s", символы с позиции 0 по 1 не включая 1. ``` Если аргумент `end` отсутствует, то идет до конца строки: ```js //+ run var str = "st*!*ringify*/!*"; alert(str.substring(2)); // ringify, символы с позиции 2 до конца ```
`substr(start [, length])`
Первый аргумент имеет такой же смысл, как и в `substring`, а второй содержит не конечную позицию, а количество символов. ```js //+ run var str = "st*!*ring*/!*ify"; str = str.substr(2,4); // ring, со 2й позиции 4 символа alert(str) ``` Если второго аргумента нет -- подразумевается "до конца строки".
`slice(start [, end])`
Возвращает часть строки от позиции `start` до, но не включая, позиции `end`. Смысл параметров -- такой же как в `substring`.
### Отрицательные аргументы Различие между `substring` и `slice` -- в том, как они работают с отрицательными и выходящими за границу строки аргументами:
`substring(start, end)`
Отрицательные аргументы интерпретируются как равные нулю. Слишком большие значения усекаются до длины строки: ```js //+ run alert( "testme".substring(-2) ); // "testme", -2 становится 0 ``` Кроме того, если start > end, то аргументы меняются местами, т.е. возвращается участок строки *между* `start` и `end`: ```js //+ run alert( "testme".substring(4, -1) ); // "test" // -1 становится 0 -> получили substring(4, 0) // 4 > 0, так что аргументы меняются местами -> substring(0, 4) = "test" ```
`slice`
Отрицательные значения отсчитываются от конца строки: ```js //+ run alert( "testme".slice(-2) ); // "me", от 2 позиции с конца ``` ```js //+ run alert( "testme".slice(1, -1) ); // "estm", от 1 позиции до первой с конца. ``` Это гораздо более удобно, чем странная логика `substring`.
Отрицательное значение первого параметра поддерживается в `substr` во всех браузерах, кроме IE8-. Если выбирать из этих трёх методов один, для использования в большинстве ситуаций -- то это будет `slice`: он и отрицательные аргументы поддерживает и работает наиболее очевидно. ## Кодировка Юникод Как мы знаем, символы сравниваются в алфавитном порядке `'А' < 'Б' < 'В' < ... < 'Я'`. Но есть несколько странностей..
  1. Почему буква `'а'` маленькая больше буквы `'Я'` большой? ```js //+ run alert( 'а' > 'Я' ); // true ```
  2. Буква `'ё'` находится в алфавите между `е` и `ж`: абвгде**ё**жз... Но почему тогда `'ё'` больше `'я'`? ```js //+ run alert( 'ё' > 'я' ); // true ```
Чтобы разобраться с этим, обратимся к внутреннему представлению строк в JavaScript. **Все строки имеют внутреннюю кодировку [Юникод](http://ru.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4).** Неважно, на каком языке написана страница, находится ли она в windows-1251 или utf-8. Внутри JavaScript-интерпретатора все строки приводятся к единому "юникодному" виду. Каждому символу соответствует свой код. Есть метод для получения символа по его коду:
String.fromCharCode(code)
Возвращает символ по коду `code`: ```js //+ run alert( String.fromCharCode(1072) ); // 'а' ```
...И метод для получения цифрового кода из символа:
str.charCodeAt(pos)
Возвращает код символа на позиции `pos`. Отсчет позиции начинается с нуля. ```js //+ run alert( "абрикос".charCodeAt(0) ); // 1072, код 'а' ```
Теперь вернемся к примерам выше. Почему сравнения `'ё' > 'я'` и `'а' > 'Я'` дают такой странный результат? Дело в том, что **символы сравниваются не по алфавиту, а по коду**. У кого код больше -- тот и больше. В юникоде есть много разных символов. Кириллическим буквам соответствует только небольшая часть из них, подробнее -- [Кириллица в Юникоде](http://ru.wikipedia.org/wiki/%D0%9A%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%D0%B8%D1%86%D0%B0_%D0%B2_%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4%D0%B5). Выведем отрезок символов юникода с кодами от `1034` до `1113`: ```js //+ run var str = ''; for (var i = 1034; i <= 1113; i++) { str += String.fromCharCode(i); } alert( str ); ``` Результат:
ЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљ
Мы можем увидеть из этого отрезка две важных вещи:
  1. **Строчные буквы идут после заглавных, поэтому они всегда больше.** В частности, `'а'(код 1072) > 'Я'(код 1071)`. То же самое происходит и в английском алфавите, там `'a' > 'Z'`.
  2. **Ряд букв, например `ё`, находятся вне основного алфавита.** В частности, маленькая буква `ё` имеет код, больший чем `я`, поэтому **`'ё'(код 1105) > 'я'(код 1103)`**. Кстати, большая буква `Ё` располагается в Unicode до `А`, поэтому **`'Ё'`(код 1025) < `'А'`(код 1040)**. Удивительно: есть буква меньше чем `А` :)
**Буква `ё` не уникальна, точки над буквой используются и в других языках, приводя к тому же результату.** Например, при работе с немецкими названиями: ```js //+ run alert( "ö" > "z" ); // true ``` [smart header="Юникод в HTML"] Кстати, если мы знаем код символа в кодировке юникод, то можем добавить его в HTML, используя "числовую ссылку" (numeric character reference). Для этого нужно написать сначала `&#`, затем код, и завершить точкой с запятой `';'`. Например, символ `'а'` в виде числовой ссылки: `а`. Если код хотят дать в 16-ричной системе счисления, то начинают с `&#x`. В юникоде есть много забавных и полезных символов, например, символ ножниц: ✂ (`✂`), дроби: ½ (`½`) ¾ (`¾`) и другие. Их можно использовать вместо картинок в дизайне. [/smart] ## Посимвольное сравнение Сравнение строк работает *лексикографически*, иначе говоря, посимвольно. Сравнение строк `s1` и `s2` обрабатывается по следующему алгоритму:
  1. Сравниваются первые символы: `s1[0]` и `s2[0]`. Если они разные, то сравниваем их и, в зависимости от результата их сравнения, возвратить `true` или `false`. Если же они одинаковые, то...
  2. Сравниваются вторые символы `s1[1]` и `s2[1]`
  3. Затем третьи `s1[2]` и `s2[2]` и так далее, пока символы не будут наконец разными, и тогда какой символ больше -- та строка и больше. Если же в какой-либо строке закончились символы, то считаем, что она меньше, а если закончились в обеих -- они равны.
Спецификация языка определяет этот алгоритм более детально. Если же говорить простыми словами, смысл алгоритма в точности соответствует порядку, по которому имена заносятся в орфографический словарь. ```js "Вася" > "Ваня" // true, т.к. начальные символы совпадают, а потом 'с' > 'н' "Дома" > "До" // true, т.к. начало совпадает, но в 1й строке больше символов ``` [warn header="Числа в виде строк сравниваются как строки"] Бывает, что числа приходят в скрипт в виде строк, например как результат `prompt`. В этом случае результат их сравнения будет неверным: ```js //+ run alert( "2" > "14" ); // true, так как это строки, и для первых символов верно "2" > "1" ``` Если хотя бы один аргумент -- не строка, то другой будет преобразован к числу: ```js //+ run alert( 2 > "14" ); // false ``` [/warn] ## Правильное сравнение Все современные браузеры, кроме IE10- (для которых нужно подключить библиотеку [Intl.JS](https://github.com/andyearnshaw/Intl.js/)) поддерживают стандарт [ECMA 402](http://www.ecma-international.org/ecma-402/1.0/ECMA-402.pdf), поддерживающий сравнение строк на разных языках, с учётом их правил. Способ использования: ```js //+ run var str = "Ёлки"; alert( str.localeCompare("Яблони") ); // -1 ``` Метод `str1.localeCompare(str2)` возвращает `-1`, если `str1 < str2`, `1`, если `str1 > str2` и `0`, если они равны. Более подробно про устройство этого метода можно будет узнать в статье [](/intl), когда это вам понадобится. ## Итого Больше информации о методах для строк можно получить в справочнике: [http://javascript.ru/String]().