Эта статья представляет собой стенограмму моей бесплатной серии YouTube об основах веб-разработки. Если вы предпочитаете смотреть, а не читать, не стесняйтесь посетить мой канал Dev Newbs.

Привет всем моим коллегам-разработчикам! Мы снова собираемся возиться с диакритическими знаками. Сегодняшняя особенность дня - это метод normalize (), который работает с разными способами представления одного и того же символа. Давайте начнем!

Метод normalize () возвращает строку в форме нормализации Unicode.

Единственный параметр, который нам нужно предоставить, - это форма. Форма может иметь одно из этих 4 значений:

  • NFC (каноническая декомпозиция, а затем каноническая композиция)
  • NFD (каноническая декомпозиция)
  • NFKC (разложение совместимости, за которым следует каноническая композиция)
  • NFKD (разложение совместимости)

Если вы опускаете значение или указываете его как undefined, вместо него используется значение по умолчанию «NFC».

Это много сокращений. Давайте проверим первые два в примере 1.

let comped =   '\u0045\u006c\u0020\u004e\u0069\u00f1\u006f';
let decomped = '\u0045\u006c\u0020\u004e\u0069\u006e\u0303\u006f';
comped + " (" + comped.length + ")"              // El Niño (7)
decomped + " (" + decomped.length + ")"          // El Niño (8)
comped == decomped                               // false
let compedNFC = comped.normalize('NFC');
let decompedNFC = decomped.normalize('NFC');
compedNFC + " (" + compedNFC.length + ")"        // El Niño (7)
decompedNFC + " (" + decompedNFC.length + ")"    // El Niño (7)
compedNFC == decompedNFC);                       // true
let compedNFD = comped.normalize('NFD');
let decompedNFD = decomped.normalize('NFD');
compedNFD + " (" + compedNFD.length + ")"        // El Niño (8)
decompedNFD + " (" + decompedNFD.length + ")"    // El Niño (8)
compedNFD == decompedNFD                         // true

Вся суть нормализации заключается в том, что мы можем написать один и тот же символ с его выделенным значением кодовой точки, но мы также можем комбинировать несколько других основных символов, чтобы получить конкретный. Именно так выглядит буква «ñ» в испанской фразе «Эль-Ниньо». Мы можем написать его, используя выделенное значение Unicode «\ u00f1», или используя два отдельных значения Unicode для основной буквы «n» плюс диакритический знак - в данном случае это «>» для буквы «n» и «\ u0303». для диакритического знака тильда над буквой «n».

Мы с вами, как потребитель результата, не можем визуально отличить разницу, но на данный момент мы начинаем использовать полученную строку для поиска или сортировки, мы можем получить то, на что не рассчитывали. По этой причине у нас есть метод normalize (), который позволяет нам, разработчикам, выбирать, как должна выглядеть результирующая строка, чтобы мы могли правильно обработать ее в нашем коде.

Первая часть показывает, что две струны не считаются равными и даже имеют разную длину.

Вторая часть применяет каноническую композицию к обеим переменным. Мы получаем две новые переменные, которые обе равны, и используем более короткую составную версию для специального символа.

Третья часть применяет противоположный принцип - каноническое разложение к обеим исходным переменным, и мы получаем еще две новые переменные. На этот раз они оба используют более длинную разложенную версию специального символа.

Я использовал термин «канонический» без более подробного объяснения того, что именно он означает. Давай исправим это сейчас. В юникоде две последовательности кодовых точек имеют «каноническую эквивалентность», если они представляют одни и те же абстрактные символы и всегда должны иметь одинаковый внешний вид и поведение - например, сортироваться одинаково.

С помощью аргументов форм «NFC» и «NFD» мы можем гарантировать, что две канонически эквивалентные строки также будут представлены одними и теми же строками, которые равны друг другу.

Затем идет «нормализация совместимости». В Юникоде две последовательности кодовых точек совместимы, если они представляют одни и те же абстрактные символы, и должны рассматриваться одинаково в некоторых, но не обязательно во всех, приложениях.

let str1 = '\uFB00';
let str2 = '\u0066\u0066';
str1 + " (" + str1.length + ")")                // ff (1)
str2 + " (" + str2.length + ")"                 // ff (2)
str1 === str2                                   // false
let str1_NFKC = str1.normalize('NFKC');         
let str2_NFKC = str2.normalize('NFKC');         
str1_NFKC + " (" + str1_NFKC.length + ")"       // ff (2)       
str2_NFKC + " (" + str2_NFKC.length + ")"       // ff (2)
str1_NFKC === str2_NFKC                         // true
let str1_NFKD = str1.normalize('NFKD');
let str2_NFKD = str2.normalize('NFKD');
str1_NFKD + " (" + str1_NFKD.length + ")"       // ff (2)     
str2_NFKD + " (" + str2_NFKD.length + ")"       // ff (2)
str1_NFKD === str2_NFKD                         // true

Можно сказать, что если две последовательности канонически эквивалентны, они также совместимы. Но не всегда можно сказать, что наоборот.

В некоторых отношениях (например, при сортировке) обе последовательности следует рассматривать как эквивалентные, а в некоторых (например, внешний вид) - нет, поэтому они не являются канонически эквивалентными.

Мы можем использовать normalize () с аргументами «NFKD» или «NFKC» для создания формы строки, которая будет одинаковой для всех совместимых строк.

Как и в этом примере, мы видим, что «ff» - это не то же самое, что две буквы «f» вместе взятые. Но если бы мы хотели отсортировать эту последовательность, мы бы хотели рассматривать этот символ как две буквы «f», но мы все же хотим сохранить их визуальное различие. Следовательно, эти две последовательности совместимы, но не канонически эквивалентны.

В последнем примере будут показаны разные результаты, которые мы получаем от применения каждой из форм к одной и той же последовательности строк.

// ORIGINAL SEQUENCE
// U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
// U+0323: COMBINING DOT BELOW
let str = '\u1E9B\u0323';
// NFC
// U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
// U+0323: COMBINING DOT BELOW
let nfc = str.normalize('NFC');
nfc + " (" + nfc.length + ")"                  // ẛ̣ (2)
[...nfc]                                       // ["ẛ", "̣"]
// NFD
// U+017F: LATIN SMALL LETTER LONG S
// U+0323: COMBINING DOT BELOW
// U+0307: COMBINING DOT ABOVE
let nfd = str.normalize('NFD');
nfd + " (" + nfd.length + ")"                  // ẛ̣ (3)
[...nfd]                                       // ["ſ", "̣", "̇"]
// NFKC
// U+1E69: LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
let nfkc = str.normalize('NFKC');
nfkc + " (" + nfkc.length + ")"                // ṩ (1)
[...nfkc]                                      // ["ṩ"]
// NFKD
// U+0073: LATIN SMALL LETTER S
// U+0323: COMBINING DOT BELOW
// U+0307: COMBINING DOT ABOVE
let nfkd = str.normalize('NFKD');
nfkd + " (" + nfkd.length + ")"                // ṩ (3)
[...nfkd]                                      // ["s", "̣", "̇"]

Каждая из форм дает немного разный результат. Первые две формы подчиняются правилу канонической эквивалентности, поэтому символ сохраняет как внешний вид, так и поведение. В последних двух случаях, которые гарантируют совместимость, визуальные изменения, но оба результата по крайней мере совместимы между собой, если не с первыми двумя формами.

Ладно, это был еще один кошмарный метод. Это было не весело, но вы поблагодарите меня позже, когда столкнетесь с этим в пустыне Интернета.

Как всегда - большое спасибо за ваше время и до скорой встречи со следующим методом.