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

String.prototype.isWellFormed()

Methode

Prüft, ob eine Zeichenkette wohlgeformtes UTF-16 ist, also keine einzelnen (ungepaarten) Surrogate enthält, und gibt <code>true</code> oder <code>false</code> zurück.

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

Signatur

String.prototype.isWellFormed()

Beschreibung

JavaScript-Strings sind intern Folgen von 16-Bit-Codeeinheiten (UTF-16). Zeichen außerhalb der Basic Multilingual Plane, etwa viele Emojis, werden als Surrogatpaar dargestellt: ein hohes Surrogat (0xD800–0xDBFF) gefolgt von einem tiefen Surrogat (0xDC00–0xDFFF). Steht eines dieser Surrogate allein, spricht man von einem einzelnen oder ungepaarten Surrogat (englisch: lone surrogate). Ein solcher String ist kein gültiges Unicode.

isWellFormed() gibt true zurück, wenn der String kein einziges ungepaartes Surrogat enthält, andernfalls false. Die Methode verändert den String nicht und ist deutlich schneller und lesbarer als eine selbst geschriebene Prüfung mit regulären Ausdrücken.

Nützlich ist die Methode immer dann, wenn Strings an Schnittstellen weitergegeben werden, die gültiges Unicode verlangen, zum Beispiel encodeURI()/encodeURIComponent() (werfen sonst einen URIError), TextEncoder (ersetzt stillschweigend durch U+FFFD), WebSockets oder Datenbank- und Netzwerkprotokolle. Typischerweise entstehen ungepaarte Surrogate, wenn Strings mit slice(), substring() oder charAt() mitten in einem Surrogatpaar zerschnitten werden.

Soll ein fehlerhafter String nicht nur erkannt, sondern auch repariert werden, verwendet man die Schwestermethode String.prototype.toWellFormed(). Sie ersetzt jedes ungepaarte Surrogat durch das Ersatzzeichen U+FFFD (�).

Rückgabewert

Typ
boolean
Beschreibung
true, wenn der String keine ungepaarten Surrogate enthält (auch beim leeren String), sonst false.

Beispiele

Grundlegende Prüfung verschiedener Strings

const strings = [
  'Hallo Welt',        // reines ASCII
  'Grüße 😀',          // vollständiges Surrogatpaar
  'ab\uD83D',          // hohes Surrogat ohne Partner
  '\uDE00cd',          // tiefes Surrogat ohne Partner
  '\uDE00\uD83D',      // falsche Reihenfolge
  ''                   // leerer String
];

for (const s of strings) {
  console.log(JSON.stringify(s), s.isWellFormed());
}
"Hallo Welt" true "Grüße 😀" true "ab\ud83d" false "\ude00cd" false "\ude00\ud83d" false "" true

URIError bei encodeURIComponent() vermeiden

const toQueryParam = (value) => {
  // encodeURIComponent wirft bei ungepaarten Surrogaten einen URIError
  const safe = value.isWellFormed() ? value : value.toWellFormed();
  return encodeURIComponent(safe);
};

console.log(toQueryParam('Café ☕'));
console.log(toQueryParam('kaputt\uD800'));
Caf%C3%A9%20%E2%98%95 kaputt%EF%BF%BD

Abgeschnittene Emojis beim Kürzen erkennen

const text = 'Tolles Angebot 🎉';

// slice arbeitet mit UTF-16-Codeeinheiten und kann ein Paar zerschneiden
const naiv = text.slice(0, 16);
console.log(naiv.isWellFormed());

// Sicherer: nach Codepunkten kürzen
const sicher = Array.from(text).slice(0, 16).join('');
console.log(sicher.isWellFormed(), sicher);
false true Tolles Angebot 🎉

Eingaben vor dem Senden validieren

const sendMessage = async (message) => {
  if (!message.isWellFormed()) {
    throw new TypeError('Nachricht enthält ungültige Unicode-Zeichen');
  }
  const response = await fetch('/api/messages', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message })
  });
  return response.ok;
};

sendMessage('Hi\uDC00').catch((err) => console.error(err.message));
Nachricht enthält ungültige Unicode-Zeichen

// Wichtig · Fallstricke

  • Kompatibilität: Erst seit ES2024 standardisiert (Chrome/Edge 111, Firefox 119, Safari 16.4, Node.js 20). Für ältere Umgebungen gibt es einen Polyfill (z. B. core-js) oder die Prüfung per Regex: !/\p{Surrogate}/u.test(str). Das funktioniert, weil im u-Modus korrekte Paare als ein einziger Codepunkt gelesen werden.
  • Wird mit einem Wert aufgerufen, der kein String ist (z. B. über String.prototype.isWellFormed.call(123)), wird dieser zuerst in einen String umgewandelt. Bei null oder undefined wird ein TypeError geworfen.
  • Keine inhaltliche Prüfung: true bedeutet nur, dass die UTF-16-Struktur gültig ist. Nicht zugewiesene Codepunkte, Steuerzeichen oder unsichtbare Zeichen (z. B. U+200B) werden nicht beanstandet. Für Sicherheitsprüfungen oder Normalisierung zusätzlich normalize() und eigene Filter verwenden.
  • TextEncoder.encode() und JSON.stringify() werfen bei ungepaarten Surrogaten keinen Fehler. Sie ersetzen sie stillschweigend durch U+FFFD bzw. geben sie als Escape-Sequenz \udXXX aus. Fehler fallen dort also oft erst beim Empfänger auf. Mit isWellFormed() lassen sie sich schon vorher erkennen.
  • Leistung: Die Laufzeit wächst linear mit der Länge des Strings. Engines können das Ergebnis allerdings zwischenspeichern und reine Latin-1-Strings sofort mit true beantworten.