В ECMAScript 6 представлены два новых флага для регулярных выражений:
yвключает режим «липкого» сопоставления.uвключает различные связанные с Unicode опции.
В данной статье объясняется влияние флага u. Эта статья будет Вам полезна, если Вы знакомы с Unicode-проблемами в Javascript.
N.B.! Поскольку при попытке публикации статьи обнаружились проблемы с отображением некоторых используемых в статье Unicode-символов, часть кода была заменена изображениями со ссылками на JSFiddle.
Влияние на синтаксис
Установка флага u в регулярном выражении позволяет использовать escape-последовательности кодовых точек ES6 Unicode (u{...}) в шаблоне.
При отсутствии флага u такие вещи, как u{1234}, технически могут по-прежнему возникать в шаблонах, но они не будут интерпретироваться как escape-последовательности кодовых точек Unicode. /u{1234}/ эквивалентно записи /u{1234}/, которая соответствует 1234 последовательным символам u вместо символа, соответствующего escape-последовательности кодовых точек U+1234.
Движок Javascript делает так из соображений совместимости. Но с установленным флагом u и такие вещи как a (где a не является escape-последовательностью) больше не будут эквивалентны a. Поэтому, даже если /a/ обрабатывается как /a/, /a/u выбрасывает ошибку, т.к. a не является зарезервированной escape-последовательностью. Это позволяет расширить функционал флага u регулярных выражений в будущей версии ECMAScript. Например, /p{Script=Greek}/u выбрасывает исключение для ES6, но может стать регулярным выражением, соответствующим всем символам греческого алфавита согласно базе данных Unicode, когда соответствующий синтаксис будет добавлен в спецификацию.
Влияние на оператор ‘.’
При отсутствии флага u, . соответствует любому символу BMP (базовая многоязыковая плоскость — Basic Multilingual Plane) за исключением разделителей строки. Когда установлен флаг ES6 u, . соответствует также астральным символам.
Влияние на квантификаторы
В регулярных выражениях Javascript доступны следующие квантификаторы (а также их вариации): *, +, ?, и {2}, {2,}, {2,4}. При отсутствии флага u, если квантификатор следует за астральным символом, он применяется только к низкому суррогату (low surrogate) этого символа.
С флагом ES6 u квантификаторы применяются к символам целиком, что справедливо даже для астральных символов.
Влияние на символьные классы
При отсутствии флага u любой заданный символьный класс может соответствовать только символам BMP. Такие вещи, как [bcd] работают как мы того ожидаем:
const regex = /^[bcd]$/;
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('d'), // true
regex.test('e') // false
);
Однако, когда в символьном классе используется астральный символ, движок Javascript обрабатывает его как два отдельных «символа»: по одному на каждую из его суррогатных половинок.
Флаг ES6 u позволяет использовать цельные астральные символы в символьных классах.
Следовательно, цельные астральные символы также могут использоваться в диапазонах символьных классов, и все будет работать как мы того ожидаем, пока установлен флаг u.
Флаг u также влияет на исключающие символьные классы. Например, /[^a]/ эквивалентно /[-x60x62-uFFFF]/, что соответствует любому символу BMP, кроме a. Но с флагом u /[^a]/u соответствует гораздо большему набору всех символов Unicode, кроме a.
Влияние на escape-последовательности
Флаг u влияет на значение escape-последовательностей D, S, и W. При отсутствии флага u, D, S, и W соответствуют любым символам BMP, которые не соответствуют d, s и w, соответственно.
С флагом u, D, S, и W также соответствуют астральным символам.
Флаг u не обращается к их обратным аналогам d, s и w. Было предложено сделать d и w (и b) более Unicode-совместимыми, но это предложение было отклонено.
Влияние на флаг i
Когда установлены флаги i и u, все символы неявно приводятся к одному регистру с помощью простого преобразования, предоставляемого стандартом Unicode, непосредственно перед их сопоставлением.
const es5regex = /[a-z]/i;
const es6regex = /[a-z]/iu;
console.log(
es5regex.test('s'), es6regex.test('s'), // true true
es5regex.test('S'), es6regex.test('S'), // true true
// Note: U+017F преобразуется в `S`.
es5regex.test('u017F'), es6regex.test('u017F'), // false true
// Note: U+212A преобразуется в `K`.
es5regex.test('u212A'), es6regex.test('u212A') // false true
);
Приведение к одному регистру (case-folding) применяется к символам в шаблоне регулярного выражения, а также к символам в сопоставляемой строке.
console.log(
/u212A/iu.test('K'), // true
/u212A/iu.test('k'), // true
/u017F/iu.test('S'), // true
/u017F/iu.test('s') // true
);
Эта логика приведения к одному регистру применяется и к escape-последовательностям w и W, что также влияет на escape-последовательности b и B. /w/iu соответствует [0-9A-Z_a-z], но также и U+017F, поскольку U+017F из сопоставляемой строки регулярного выражения преобразуется (canonicalizes) в S. То же самое касается U+212A и K. Таким образом, /W/iu эквивалентно /[^0-9a-zA-Z_u{017F}u{212A}]/u.
console.log(
/w/iu.test('u017F'), // true
/w/iu.test('u212A'), // true
/W/iu.test('u017F'), // false
/W/iu.test('u212A'), // false
/W/iu.test('s'), // false
/W/iu.test('S'), // false
/W/iu.test('K'), // false
/W/iu.test('k'), // false
/b/iu.test('u017F'), // true
/b/iu.test('u212A'), // true
/b/iu.test('s'), // true
/b/iu.test('S'), // true
/B/iu.test('u017F'), // false
/B/iu.test('u212A'), // false
/B/iu.test('s'), // false
/B/iu.test('S'), // false
/B/iu.test('K'), // false
/B/iu.test('k') // false
);
Влияние на HTML-документы
Верьте или нет, но флаг u также влияет и на документы HTML.
Атрибут pattern для элементов input и textarea позволяет вам указать регулярное выражение для проверки ввода пользователя. Затем браузер обеспечивает вас стилями и скриптами для создания поведения, основанного на достоверности ввода.
Флаг u всегда включен для регулярных выражений, скомпилированных через HTML атрибут pattern. Вот демонстрационный пример.
Поддержка
На данный момент флаг ES6 u для регулярных выражений доступен в стабильных версиях всех основных браузеров, кроме Safari. Браузеры постепенно начинают использовать этот функционал для HTML атрибута pattern.
| Browser(s) | JavaScript engine | u flag |
u flag for pattern attribute |
|---|---|---|---|
| Edge | Chakra | issue #1102227 + issue #517 + issue #1181 | issue #7113940 |
| Firefox | Spidermonkey | bug #1135377 + bug #1281739 | bug #1227906 |
| Chrome/Opera | V8 | V8 issue #2952 + issue #5080 | issue #535441 |
| WebKit | JavaScriptCore | bug #154842 + bug #151597 + bug #158505 | bug #151598 |
Рекомендации для разработчиков
- С этого момента используйте флаг
uдля каждого регулярного выражения, которое вы пишете. - … но не добавляйте флаг
uслепо в существующие регулярные выражения, поскольку это может неявным образом изменить их смысл. - Избегайте комбинирования флагов
uиi. Лучше явно включать в регулярное выражение символы всех регистров, чем страдать от неявного программного приведения символов к одному регистру. - Используйте транспилер, чтобы убедиться, что ваш код работает везде, включая устаревшие окружения.
Преобразование (transpiling) Unicode ES6 регулярных выражений в ES5
Мной создан regexpu, транспилер, который преобразовывает регулярные выражения Unicode ES6 в эквивалентный код ES5, который работает уже сегодня. Это позволит вам играться с новым, развивающимся функционалом.
Полномасштабные транспилеры ES6/ES7, такие как Traceur и Babel, зависят от regexpu при транспиляции u. Дайте мне знать, если вам удастся это сломать.
Автор: Олег Лазарев












