Start · Sprachen · JavaScript · Referenz · String.prototype.toWellFormed()

String.prototype.toWellFormed()

Methode

Gibt eine Kopie des Strings zurück, in der alle einsamen Surrogate durch das Unicode-Ersatzzeichen <code>U+FFFD</code> (�) ersetzt sind.

seit JavaScript ES2024 (Baseline 2023: Chrome 111, Firefox 119, Sa Kategorie: method-property

Signatur

String.prototype.toWellFormed()

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

Typ
string
Beschreibung
Ein neuer String, in dem jedes einsame Surrogat durch 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());
}
ab� ab�c �ab c�ab abc ab😄c

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);
URIError https://example.com/suche?q=Hallo%20%EF%BF%BD

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 }));
false Super Artikel 👍� true {"vorschau":"Super Artikel 👍�"}

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'));
x�y

// Wichtig · Fallstricke

  • Verlust von Daten: Die Ersetzung durch U+FFFD ist nicht umkehrbar. Wenn du das ursprüngliche Surrogat noch brauchst, z. B. um zwei Teilstücke wieder zusammenzusetzen, rufe toWellFormed() 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 es normalize().
  • Verhalten anderer APIs: TextEncoder.encode() ersetzt einsame Surrogate selbst durch U+FFFD. encodeURIComponent() wirft dagegen einen URIError. JSON.stringify() gibt einsame Surrogate seit ES2019 als Escape-Sequenz \udXXX aus, 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() auf null oder undefined angewendet, wirft sie einen TypeError. Andere Werte werden vorher in einen String umgewandelt.