Signatur
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
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());
}
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'));
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);
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));
// 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 imu-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. Beinulloderundefinedwird einTypeErrorgeworfen. - Keine inhaltliche Prüfung:
truebedeutet 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ätzlichnormalize()und eigene Filter verwenden. TextEncoder.encode()undJSON.stringify()werfen bei ungepaarten Surrogaten keinen Fehler. Sie ersetzen sie stillschweigend durchU+FFFDbzw. geben sie als Escape-Sequenz\udXXXaus. Fehler fallen dort also oft erst beim Empfänger auf. MitisWellFormed()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
truebeantworten.