Egy űrlapon „pontosan egy emojit” kérni ártatlan feladatnak hangzik, egészen addig, amíg valaki beilleszt egy család emojit. A 👨👩👧👦 a felhasználó szemében egy jel, a JavaScript szemében viszont több Unicode-kódpontból és ZWJ-ből összerakott sorozat. A /^.$/u tehát nem fogja elfogadni. A reguláris kifejezések v flagje pont erre a fajta szakadékra ad új eszközöket – és közben néhány régi Unicode-szabály jelentését is átírja.

Nem csak a u flag új verziója
A nyelvi specifikáció a v-t UnicodeSets módnak nevezi. A u-hoz hasonlóan Unicode-tudatosan értelmezi a mintát, de a karakterosztályokat valódi halmazműveletekkel bővíti. Metszetet írhatunk &&-tel, kivonást ---szal. Ez nem pusztán esztétika: a korábbi, egymásra pakolt negatív lookaheadek helyett a szabály ott van, ahol a karakterhalmazt olvassuk.
const latinBetuk = /^[\p{Script_Extensions=Latin}&&\p{Letter}]+$/v;
console.log(latinBetuk.test("árvíztűrő")); // true
console.log(latinBetuk.test("Δelta")); // false
console.log(latinBetuk.test("abc42")); // false
Itt az egyik oldal a latin írásrendszerhez kapcsolódó karakterek halmaza, a másik a betűké; a metszetük kell. A v módú karakterosztályok ennél összetettebb halmazokat is tudnak, de nem érdemes minden validátort ilyen matematikai rejtvénnyé változtatni. Akkor jó, ha a domain szabálya tényleg egy karakterhalmaz.
Itt rögtön van egy kevésbé látványos csavar is: a v nem normalizálja a szöveget. Az é lehet egy kódpont, vagy e és egy külön kombináló ékezet; a fenti „csak betű” minta az utóbbit elutasítja. Névmezőben ez váratlan hiba, nem szigorú validáció. Ha a domainben helyes a kanonikus összehasonlítás, validálás előtt normalizálj NFC-re. Jelszót, tokent vagy aláírt üzenetet viszont ne alakíts át csak azért, hogy a regexed kényelmesebben érezze magát.
const dekomponalt = "e\u0301";
console.log(latinBetuk.test(dekomponalt)); // false
console.log(latinBetuk.test(dekomponalt.normalize("NFC"))); // true
A meglepetés: egy \p néha nem karaktert talál
A v mód Unicode-tulajdonságai között úgynevezett „string property” is lehet. Az RGI_Emoji például egyetlen regex-találatként képes kezelni egy szabványos emoji-sorozatot. Ezt a Unicode property escape-ek leírása is külön kiemeli: ilyen tulajdonság csak pozitív \p-vel használható.
const egyEmoji = /^(?:\p{RGI_Emoji})$/v;
console.log(egyEmoji.test("👨👩👧👦")); // true
console.log(egyEmoji.test("a👨👩👧👦")); // false
Na, ezt nem tudtam: a „karakterosztály” mögötti mechanizmus itt több kódpont hosszú sztringet is fogyaszthat. Ez mégsem univerzális grapheme-cluster számláló. Az RGI_Emoji az Unicode által nyilvántartott emoji-sorozatokra vonatkozik; tetszőleges, felhasználó által összerakott látható karakterek mérésére továbbra is az Intl.Segmenter a tisztább választás. Emoji-only mezőnél viszont sokkal pontosabb, mint egy tiltólista vagy a „két UTF-16 egység biztos emoji” iskola.
A i és a negálás csapdája
A v nem teljesen kompatibilis átnevezése a u-nak. Unicode property escape és kis-nagybetű-független keresés együtt egy ritka, de lényeges eltérést hoz. u módban a motor előbb kiegészíti a \P{Lowercase_Letter} halmazt, és csak utána hajtja végre a case foldingot; v-ben a case folding előbb történik. Emiatt ez a program más eredményt ad:
console.log(/\P{Lowercase_Letter}/iu.test("a")); // true
console.log(/\P{Lowercase_Letter}/iv.test("a")); // false
A részletes szabályt és a pontos példát az MDN dokumentációja is rögzíti. A tanulság prózaibb, mint maga az algoritmus: ha egy régi u-s mintát v-re cserélsz, a \P + i kombinációt teszteld valódi mintaadatokkal. Itt a „szigorúbb Unicode” nem garantálja ugyanazt a validációt.
Productionben: célzottan, nem divatból
Én productionben használom, ha a terméknek tényleg Unicode-halmazon alapuló szabálya van: névleges betűkészlet, emoji-reakció vagy dokumentált azonosító-formátum. Meglévő u-s regexek tömeges átírására nem. A v szebb lehet, de a rövidebb regex nem automatikusan érthetőbb regex; az olvashatóság továbbra is emberi feature.
Ha a kliensköröd nem garantálja a v támogatását, ne küldj le olyan bundle-t, amely már a parse-oláskor elhasal. Feature detecthez elég egy fordítási próba, a szerveroldali validációt pedig ne váltsd ki vele:
const hasUnicodeSets = (() => {
try {
new RegExp("\\p{RGI_Emoji}", "v");
return true;
} catch {
return false;
}
})();
Dinamikusan összerakott mintánál a bemenetet továbbra is escape-elni kell; erre már van szabványos RegExp.escape(). A v jó eszköz szöveg- és emoji-szabályokhoz, de nem XSS-védelem, nem jogosultságkezelés és nem a backend helyett dolgozó őr. Egy regexnek is lehet túl nagy a hatásköre – csak általában nem kap hozzá architektúra-diagramot.