Signatur
Beschreibung
JavaScript-Strings sind intern Folgen von UTF-16-Codeeinheiten. Zeichen außerhalb der Basic Multilingual Plane (z. B. viele Emojis) bestehen aus einem Surrogat-Paar: einem hohen Surrogat (0xD800–0xDBFF), auf das ein niedriges Surrogat (0xDC00–0xDFFF) folgt. Steht ein Surrogat allein, also ohne passenden Partner, spricht man von einem einsamen Surrogat. Ein solcher String ist nicht „wohlgeformt“ und lässt sich nicht verlustfrei als gültiges UTF-8 oder UTF-16 darstellen.
toWellFormed() erzeugt einen neuen String, in dem jedes einsame Surrogat durch U+FFFD (REPLACEMENT CHARACTER) ersetzt wird. Gültige Surrogat-Paare und alle anderen Zeichen bleiben unverändert. Der ursprüngliche String wird nicht verändert, denn Strings sind in JavaScript unveränderlich.
Nützlich ist die Methode immer dann, wenn Strings an APIs weitergegeben werden, die wohlgeformtes Unicode verlangen. Dazu gehören encodeURI() und encodeURIComponent(), die bei einsamen Surrogaten einen URIError werfen. Auch TextEncoder, das Einsame stillschweigend ersetzt, sowie Netzwerk-, Datei- oder Datenbankschnittstellen zählen dazu. Häufig entstehen solche Strings, wenn man Texte mit slice(), substring() oder charAt() mitten in einem Emoji abschneidet.
Wenn du nur prüfen willst, ob ein String wohlgeformt ist, ohne ihn zu verändern, nimm String.prototype.isWellFormed().
Rückgabewert
U+FFFD ersetzt ist. Ist der String bereits wohlgeformt, wird eine inhaltsgleiche Kopie zurückgegeben.Beispiele
Einsame Surrogate ersetzen
const strings = [
'ab\uD800', // einsames hohes Surrogat am Ende
'ab\uD800c', // einsames hohes Surrogat in der Mitte
'\uDFFFab', // einsames niedriges Surrogat am Anfang
'c\uDFFFab', // einsames niedriges Surrogat in der Mitte
'abc', // wohlgeformt
'ab\uD83D\uDE04c' // gültiges Surrogat-Paar (😄)
];
for (const str of strings) {
console.log(str.toWellFormed());
}
URIError bei encodeURIComponent vermeiden
const eingabe = 'Hallo \uD83D'; // abgeschnittenes Emoji
try {
encodeURIComponent(eingabe);
} catch (err) {
console.log(err.name); // URIError
}
// Vorher bereinigen:
const url = `https://example.com/suche?q=${encodeURIComponent(eingabe.toWellFormed())}`;
console.log(url);
Gekürzte Vorschautexte absichern
const kommentar = 'Super Artikel 👍👍👍';
// slice() arbeitet mit UTF-16-Codeeinheiten und kann ein Emoji zerteilen
const vorschau = kommentar.slice(0, 15);
console.log(vorschau.isWellFormed());
const sicher = vorschau.toWellFormed();
console.log(sicher, sicher.isWellFormed());
// Speicherbar, z. B. als JSON an eine API senden
console.log(JSON.stringify({ vorschau: sicher }));
Fallback für ältere Umgebungen
const toWellFormed = (str) =>
typeof str.toWellFormed === 'function'
? str.toWellFormed()
: str.replace(
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,
'\uFFFD'
);
console.log(toWellFormed('x\uDC00y'));
// Wichtig · Fallstricke
- Verlust von Daten: Die Ersetzung durch
U+FFFDist nicht umkehrbar. Wenn du das ursprüngliche Surrogat noch brauchst, z. B. um zwei Teilstücke wieder zusammenzusetzen, rufetoWellFormed()erst ganz am Ende auf. - Länge bleibt gleich: Jede ersetzte Codeeinheit wird durch genau eine andere ersetzt,
lengthändert sich also nicht. Die UTF-8-Bytegröße kann dagegen abweichen. - Keine Unicode-Normalisierung:
toWellFormed()repariert nur Surrogate. Kombinierte Zeichen und ungültige Grapheme bleiben unberührt. Für Normalisierung gibt esnormalize(). - Verhalten anderer APIs:
TextEncoder.encode()ersetzt einsame Surrogate selbst durchU+FFFD.encodeURIComponent()wirft dagegen einenURIError.JSON.stringify()gibt einsame Surrogate seit ES2019 als Escape-Sequenz\udXXXaus, statt sie zu ersetzen. - Kompatibilität: Die Methode kam mit ES2024. Sie ist verfügbar ab Chrome/Edge 111, Firefox 119, Safari 16.4 und Node.js 20. Für ältere Umgebungen gibt es einen Polyfill (z. B.
core-js) oder den Regex-Fallback oben. Lookbehind-Regex braucht dabei ES2018. - Wird die Methode über
call()aufnulloderundefinedangewendet, wirft sie einenTypeError. Andere Werte werden vorher in einen String umgewandelt.