Two separate problems hide behind "comparing strings".
Order. <, === and Array.prototype.sort() without a comparator compare code units numerically. That puts every uppercase ASCII letter before every lowercase one and accented letters after z, which is wrong for people. Language-aware order comes from Intl.Collator (ECMA-402). a.localeCompare(b, locale, options) is the one-off form. Pass the collator's compare straight to sort, because ECMA-402 defines it as a getter returning a function already bound to the collator.
Locale changes the answer: German sorts ä with a, Swedish sorts it after z. Options tune what counts as different:
sensitivity:"base"(only base letters differ),"accent","case","variant"(all differences matter).numeric: true: compares digit runs as numbers, sofile2comes beforefile10.caseFirst:"upper"or"lower".
Create the collator once and reuse it. MDN recommends this over localeCompare when comparing many strings.
Equality. One visible character can have several code point sequences. "é" is U+00E9 or e + U+0301. They render the same but === is false. normalize() rewrites a string into a Unicode normalization form: NFC (composed, the default), NFD (decomposed), and the compatibility forms NFKC and NFKD, which also fold things like the fi ligature into fi. Normalize both sides to the same form before an exact comparison, or before storing keys such as usernames. An invalid form name throws a RangeError.
Intl.Collator is required to treat canonically equivalent strings as equal, so it also handles the é case without manual normalization.
Results depend on the engine's locale data (ICU in V8 and Node), so don't hard-code sort order assumptions beyond what the locale defines.