Respuesta técnica directa
Las cadenas de JavaScript son secuencias de unidades de código UTF-16 de 16 bits, no caracteres Unicode individuales ni símbolos percibidos por el usuario. Los caracteres suplementarios fuera del Basic Multilingual Plane (como los emojis y los ideogramas poco frecuentes) requieren dos unidades de código UTF-16 (un par sustituto), mientras que los grafemas compuestos requieren varios puntos de código.
La diferencia entre unidades de código UTF-16, puntos de código Unicode y clústeres de grafemas en JavaScript
const emoji = '😀';
// 1. String.prototype.length counts UTF-16 code units
console.log(emoji.length); // 2
// 2. String iterator ([...str]) counts Unicode code points
console.log([...emoji].length); // 1
// 3. Composite emoji with Zero-Width Joiner (ZWJ)
const family = '👨👦';
console.log(family.length); // 5 code units
console.log([...family].length); // 3 code points
// 4. Intl.Segmenter counts user-perceived grapheme clusters
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
console.log([...segmenter.segment(family)].length); // 1 user character
2
1
5
3
1
Por qué ocurre esto: el modelo de cadenas de ECMAScript
Para entender el comportamiento de las cadenas de JavaScript, los desarrolladores deben comprender las tres capas distintas de representación Unicode en el navegador y en Node.js.
1. El legado histórico de UTF-16
2. Unidades de código frente a puntos de código
3. Puntos de código frente a clústeres de grafemas percibidos por el usuario
4. Sustitutos huérfanos y cadenas mal formadas
Modelo de ejecución del lenguaje y del entorno
V8 utiliza una optimización de representación interna: las cadenas que contienen solo caracteres Latin-1 (U+0000..U+00FF) se almacenan como matrices de 1 byte (SeqOneByteString). Las cadenas que contienen cualquier carácter por encima de U+00FF se promocionan a matrices UTF-16 de 2 bytes (SeqTwoByteString).
Errores comunes frente a patrones de producción
Aprenda qué patrones fallan en producción y cuáles son las alternativas modernas conformes con los estándares.
Contar caracteres visibles para el usuario
Contador de caracteres en formularios de interfaz, límites de SMS, campos de biografía.const str = '👨💻';
console.log(str.length);
// Returns 5 (UTF-16 code units)
const str = '👨💻';
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
console.log([...segmenter.segment(str)].length);
// Returns 1 (true user-perceived character)
Iterar los caracteres de una cadena
Analizar tokens, construir transformadores de cadenas personalizados.const text = '🔥hello';
for (let i = 0; i < text.length; i++) {
console.log(text[i]); // splits 🔥 into \uD83D and \uDD25!
}
const chars = text.split(''); // ['\uD83D', '\uDD25', 'h', 'e', 'l', 'l', 'o']
const text = '🔥hello';
for (const char of text) {
console.log(char); // '🔥', 'h', 'e', 'l', 'l', 'o'
}
const chars = [...text]; // ['🔥', 'h', 'e', 'l', 'l', 'o']
Leer puntos de código numéricos
Convertidores de codificación de caracteres, hash criptográfico, métricas de fuentes.const char = '😀';
console.log(char.charCodeAt(0).toString(16));
// Returns 'd83d' (High surrogate only!)
const char = '😀';
console.log(char.codePointAt(0).toString(16));
// Returns '1f600' (True Unicode code point!)
Escribir secuencias de escape Unicode
Literales de código fuente, payloads JSON, definiciones de expresiones regulares.const fire = '\uD83D\uDD25'; // 🔥
// Prone to transposition, hard to read, manual math needed
const fire = '\u{1F525}'; // 🔥
const smile = '\u{1F600}'; // 😀
Matriz de casos límite e invariantes
Una matriz de pruebas diversa entre caracteres que compara el comportamiento de los caracteres en ASCII, marcas combinantes, emojis, banderas y fragmentos sustitutos.
| Caso de prueba | Glifo de entrada | Unidades de código | Puntos de código | Grafemas | Categoría | Explicación técnica |
|---|---|---|---|---|---|---|
| Letra latina ASCII | A |
1
|
1
|
1
|
Basic Latin | U+0041 cabe en un solo byte de 7 bits, una unidad de código de 16 bits y un punto de código. |
| Acento precompuesto | é |
1
|
1
|
1
|
Latin-1 Supplement | U+00E9 acento agudo precompuesto en el BMP. 1 unidad de código, 1 punto de código, 1 grafema. |
| Acento descompuesto | é |
2
|
2
|
1
|
Combining Mark | Letra base e (U+0065) + acento agudo combinante (U+0301). 2 puntos de código se renderizan como 1 grafema visual. |
| Símbolo de moneda | € |
1
|
1
|
1
|
Currency | Signo del euro U+20AC ubicado en el BMP. 1 unidad de código, 1 punto de código, 1 grafema. |
| Emoji estándar | 😀 |
2
|
1
|
1
|
Supplementary Plane | Cara sonriente U+1F600 en el plano 1. Codificado como 2 unidades de código UTF-16 (D83D DE00). |
| Secuencia de emoji con ZWJ | 👨👦 |
5
|
3
|
1
|
Emoji Sequence | Hombre (U+1F468) + ZWJ (U+200D) + niño (U+1F466). 3 puntos de código unidos como 1 glifo de familia. |
| Bandera del arcoíris | 🏳️🌈 |
6
|
4
|
1
|
Emoji Sequence | Bandera blanca (U+1F3F3) + selector de variación 16 (U+FE0F) + ZWJ (U+200D) + arcoíris (U+1F308). |
| Bandera nacional (indicadores regionales) | 🇺🇳 |
4
|
2
|
1
|
Flag Sequence | Indicador regional U (U+1F1FA) + indicador regional N (U+1F1F3). 2 puntos de código, 1 grafema. |
| Sustituto alto huérfano |
1
|
1
|
1
|
Surrogate Fragment | Sustituto alto aislado 0xD83D. Válido en la memoria de ECMAScript, pero mal formado en UTF-8. |
Core API Reference
Standard library methods, runtime signatures, and Unicode semantics.
str.codePointAt(index: number): number | undefined
Devuelve el valor del punto de código Unicode (entero) en el índice de unidad de código UTF-16 especificado.
String.fromCodePoint(...codePoints: number[]): string
Crea una cadena a partir de una secuencia de puntos de código Unicode de 21 bits.
new Intl.Segmenter(locales?: string | string[], options?: { granularity: "grapheme" | "word" | "sentence" })
Proporciona una segmentación de texto consciente de la configuración regional, lo que permite una iteración fiable de los clústeres de grafemas percibidos por el usuario.
str.normalize(form?: "NFC" | "NFD" | "NFKC" | "NFKD"): string
Devuelve la forma de normalización Unicode de la cadena que realiza la llamada.
new RegExp(pattern, "v")
Indicador UnicodeSets de expresiones regulares de ECMAScript 2024 que habilita operaciones de conjuntos y propiedades de cadena.
Inspector interactivo de cadenas y Unicode en JavaScript
Pruebe cualquier cadena o secuencia de emojis en tiempo real para inspeccionar sus unidades de código UTF-16, puntos de código Unicode y clústeres de grafemas en su navegador.
Desglose de puntos de código:
Procedencia de estándares y fuentes
Todos los invariantes técnicos, las API y los comportamientos de esta guía se verifican contra las especificaciones primarias oficiales.
ECMAScript 2024 Language Specification (ECMA-262 15th Edition)
Cláusula: §6.1.4 The String Type & §22.1 String Objects
ECMAScript Internationalization API (ECMA-402)
Cláusula: §18 Intl.Segmenter Objects
Unicode Standard Annex #29: Unicode Text Segmentation
Cláusula: §3 Grapheme Cluster Boundaries
Unicode Standard Annex #15: Unicode Normalization Forms
Cláusula: §1 Normalization Forms
JavaScript String Reference & UTF-16 Representation
Cláusula: UTF-16 Characters, Unicode Code Points, and Grapheme Clusters