En JavaScript, los datos textuales son almacenados como strings (cadena de caracteres). No hay un tipo de datos separado para caracteres unitarios.
El formato interno para strings es siempre UTF-16, no est vinculado a la codificacin de la pgina.
Comillas
Recordemos los tipos de comillas.
Los strings pueden estar entre comillas simples, comillas dobles o backticks (acento grave):
let single = 'comillas simples';
let double = "comillas dobles";
let backticks = `backticks`;
Comillas simples y dobles son esencialmente lo mismo. En cambio, los backticks nos permiten adems ingresar expresiones dentro del string envolvindolos en ${}:
function sum(a, b) {
return a + b;
}
alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.
Otra ventaja de usar backticks es que nos permiten extender en mltiples lneas el string:
let guestList = `Invitados:
* Juan
* Pedro
* Maria
`;
alert(guestList); // una lista de invitados, en mltiples lneas
Se ve natural, no es cierto? Pero las comillas simples y dobles no funcionan de esa manera.
Si intentamos usar comillas simples o dobles de la misma forma, obtendremos un error:
let guestList = "Invitados: // Error: Unexpected token ILLEGAL
* Juan";
Las comillas simples y dobles provienen de la creacin de lenguajes en tiempos ancestrales, cuando la necesidad de mltiples lneas no era tomada en cuenta. Los backticks aparecieron mucho despus y por ende son ms verstiles.
Los backticks adems nos permiten especificar una funcin de plantilla antes del primer backtick. La sintaxis es: func`string`. La funcin func es llamada automticamente, recibe el string y la expresin insertada, y los puede procesar. Eso se llama plantillas etiquetadas. Es raro verlo implementado, pero puedes leer ms sobre esto en el manual.
Caracteres especiales
Es posible crear strings de mltiples lneas usando comillas simples, usando un llamado carcter de nueva lnea, escrito como \n, lo que denota un salto de lnea:
let guestList = 'Invitados:\n * Juan\n * Pedro\n * Maria';
alert(guestList); // lista de invitados en mltiples lneas, igual a la de ms arriba
Como ejemplo ms simple, estas dos lneas son iguales, pero escritas en forma diferente:
let str1 = "Hello\nWorld"; // dos lneas usando el "smbolo de nueva lnea"
// dos lneas usando nueva lnea normal y backticks
let str2 = `Hello
World`;
alert(str1 == str2); // true
Existen otros caracteres especiales, menos comunes.
| Carcter | Descripcin |
|---|---|
\n |
Nueva lnea |
\r |
En Windows, los archivos de texto usan una combinacin de dos caracteres \r\n para representar un corte de lnea, mientras que en otros SO es simplemente \n. Esto es por razones histricas, la mayora del software para Windows tambin reconoce \n. |
\',\",\` |
Comillas |
\\ |
Barra invertida |
\t |
Tabulacin |
\b, \f, \v |
Retroceso, avance de formulario, tabulacin vertical Se mencionan para ser exhaustivos. Vienen de muy viejos tiempos y no se usan actualmente (puedes olvidarlos ya). |
Como puedes ver, todos los caracteres especiales empiezan con la barra invertida \. Se lo llama carcter de escape.
Y como es tan especial, si necesitamos mostrar el verdadero carcter \ dentro de un string, necesitamos duplicarlo:
alert( `La barra invertida: \\` ); // La barra invertida: \
Las llamadas comillas escapadas \', \", \` se usan para insertar una comilla en un string entrecomillado con el mismo tipo de comilla.
Por ejemplo:
alert('Yo soy la \'morsa\'!'); // Yo soy la 'morsa'!
Como puedes ver, debimos anteponer un carcter de escape \ antes de cada comilla ya que de otra manera hubiera indicado el final del string.
Obviamente, solo necesitan ser escapadas las comillas que son iguales a las que estn rodeando al string. Una solucin ms elegante es cambiar a comillas dobles o backticks:
alert("Yo soy la 'morsa'!"); // Yo soy la 'morsa'!
Adems de estos caracteres especiales, tambin hay una notacin especial para cdigos Unicode \u que se usa raramente. Los cubrimos en el captulo opcional acerca de Unicode.
Largo del string
La propiedad length contiene el largo del string:
alert(`Mi\n`.length); // 3
Nota que \n es un solo carcter, por lo que el largo total es 3.
length es una propiedadQuienes tienen experiencia en otros lenguajes pueden cometer el error de escribir str.length() en vez de str.length. Eso no funciona.
Nota que str.length es una propiedad numrica, no una funcin. No hay que agregar parntesis despus de ella. No es .length(), sino .length.
Accediendo caracteres
Para acceder a un carcter en la posicin pos, se debe usar corchetes, [pos], o llamar al mtodo str.at(pos). El primer carcter comienza desde la posicin cero:
let str = `Hola`;
// el primer carcter
alert( str[0] ); // H
alert( str.at(0) ); // H
// el ltimo carcter
alert( str[str.length - 1] ); // a
alert( str.at(-1) );
Como puedes ver, el mtodo .at(pos) tiene el beneficio de permitir una posicin negativa. Si pos es negativa, se cuenta desde el final del string.
As, .at(-1) significa el ltimo carcter, y .at(-2) es el anterior a l, etc.
Los corchetes siempre devuelven undefined para ndices negativos:
let str = `Hola`;
alert( str[-2] ); // undefined
alert( str.at(-2) ); // l
Podemos adems iterar sobre los caracteres usando for..of:
for (let char of 'Hola') {
alert(char); // H,o,l,a (char se convierte en "H", luego "o", luego "l", etc.)
}
Los strings son inmutables
Los strings no pueden ser modificados en JavaScript. Es imposible modificar un carcter.
Intentmoslo para demostrar que no funciona:
let str = 'Hola';
str[0] = 'h'; // error
alert(str[0]); // no funciona
Lo usual para resolverlo es crear un nuevo string y asignarlo a str reemplazando el string completo.
Por ejemplo:
let str = 'Hola';
str = 'h' + str[1] + str[2] + str[3]; // reemplaza el string
alert( str ); // hola
En las secciones siguientes veremos ms ejemplos de esto.
Cambiando capitalizacin
Los mtodos toLowerCase() y toUpperCase() cambian los caracteres a minscula y mayscula respectivamente:
alert('Interfaz'.toUpperCase()); // INTERFAZ
alert('Interfaz'.toLowerCase()); // interfaz
Si queremos un solo carcter en minscula:
alert('Interfaz'[0].toLowerCase()); // 'i'
Buscando una subcadena de caracteres
Existen muchas formas de buscar por subcadenas de caracteres dentro de una cadena completa.
str.indexOf
El primer mtodo es str.indexOf(substr, pos).
Este busca un substr en str, comenzando desde la posicin entregada pos, y retorna la posicin donde es encontrada la coincidencia o -1 en caso de no encontrar nada.
Por ejemplo:
let str = 'Widget con id';
alert(str.indexOf('Widget')); // 0, ya que 'Widget' es encontrado al comienzo
alert(str.indexOf('widget')); // -1, no es encontrado, la bsqueda toma en cuenta minsculas y maysculas.
alert(str.indexOf('id')); // 1, "id" es encontrado en la posicin 1 (..idget con id)
El segundo parmetro es opcional y nos permite buscar desde la posicin entregada.
Por ejemplo, la primera ocurrencia de "id" es en la posicin 1. Para buscar por la siguiente ocurrencia, comencemos a buscar desde la posicin 2:
let str = 'Widget con id';
alert(str.indexOf('id', 2)); // 11
Si estamos interesados en todas las ocurrencias, podemos correr indexOf en un bucle. Cada nuevo llamado es hecho utilizando la posicin posterior a la encontrada anteriormente:
let str = 'Astuto como un zorro, fuerte como un buey';
let target = 'como'; // busquemos por l
let pos = 0;
while (true) {
let foundPos = str.indexOf(target, pos);
if (foundPos == -1) break;
alert(`Encontrado en ${foundPos}`);
pos = foundPos + 1; // continuar la bsqueda desde la siguiente posicin
}
Podemos escribir el mismo algoritmo, pero ms corto:
let str = 'Astuto como un zorro, fuerte como un buey';
let target = "como";
let pos = -1;
while ((pos = str.indexOf(target, pos + 1)) != -1) {
alert( pos );
}
str.lastIndexOf(substr, position)Tambin hay un mtodo similar str.lastIndexOf(substr, position) que busca desde el final del string hasta el comienzo.
Este imprimir las ocurrencias en orden invertido.
Existe un leve inconveniente con indexOf en la prueba if. No podemos utilizarlo en el if como sigue:
let str = "Widget con id";
if (str.indexOf("Widget")) {
alert("Lo encontramos"); // no funciona!
}
La alerta en el ejemplo anterior no se muestra ya que str.indexOf("Widget") retorna 0 (lo que significa que encontr el string en la posicin inicial). Eos correcto, pero if considera 0 como falso.
Por ello debemos preguntar por -1:
let str = "Widget con id";
if (str.indexOf("Widget") != -1) {
alert("Lo encontramos"); // ahora funciona!
}
includes, startsWith, endsWith
El mtodo ms moderno str.includes(substr, pos) devuelve true o false si str contiene substr o no.
Es la opcin adecuada si lo que necesitamos es verificar que exista, pero no su posicin.
alert('Widget con id'.includes('Widget')); // true
alert('Hola'.includes('Adis')); // false
El segundo argumento opcional de str.includes es la posicin desde donde comienza a buscar:
alert('Midget'.includes('id')); // true
alert('Midget'.includes('id', 3)); // false, desde la posicin 3 no hay "id"
Los mtodos str.startsWith (comienza con) y str.endsWith (termina con) hacen exactamente lo que dicen:
alert( "Widget".startsWith("Wid") ); // true, "Widget" comienza con "Wid"
alert( "Widget".endsWith("get") ); // true, "Widget" termina con "get"
Obteniendo un substring
Existen 3 mtodos en JavaScript para obtener un substring: substring, substr y slice.
str.slice(comienzo [, final])-
Retorna la parte del string desde
comienzohasta (pero sin incluir)final.Por ejemplo:
let str = "stringify"; alert( str.slice(0, 5) ); // 'strin', el substring desde 0 hasta 5 (sin incluir 5) alert( str.slice(0, 1) ); // 's', desde 0 hasta 1, pero sin incluir 1, por lo que slo el carcter en 0Si no existe el segundo argumento, entonces
sliceva hasta el final del string:let str = "stringify"; alert( str.slice(2) ); // ringify, desde la 2nda posicin hasta el finalTambin son posibles valores negativos para
comienzo/final. Estos indican que la posicin es contada desde el final del string.let str = "stringify"; // comienza en la 4ta posicin desde la derecha, finaliza en la 1era posicin desde la derecha alert( str.slice(-4, -1) ); // 'gif' str.substring(comienzo [, final])-
Devuelve la parte del string entre
comienzoyfinal(no incluyendofinal).Esto es casi lo mismo que
slice, pero permite quecomienzosea mayor quefinal(en este caso solo intercambia los valores decomienzoyfinal).Por ejemplo:
let str = "stringify"; // esto es lo mismo para substring alert( str.substring(2, 6) ); // "ring" alert( str.substring(6, 2) ); // "ring" // ...pero no para slice: alert( str.slice(2, 6) ); // "ring" (lo mismo) alert( str.slice(6, 2) ); // "" (un string vaco)Los argumentos negativos (al contrario de slice) no son soportados, son tratados como
0. str.substr(comienzo [, largo])-
Retorna la parte del string desde
comienzo, con ellargodado.A diferencia de los mtodos anteriores, este nos permite especificar el
largoen lugar de la posicin final:let str = "stringify"; alert( str.substr(2, 4) ); // ring, desde la 2nda posicin toma 4 caracteresEl primer argumento puede ser negativo, para contar desde el final:
let str = "stringify"; alert( str.substr(-4, 2) ); // gi, desde la 4ta posicin toma 2 caracteresEste mtodo reside en el Anexo B de la especificacin del lenguaje. Esto significa que solo necesitan darle soporte los motores Javascript de los navegadores, y no es recomendable su uso. Pero en la prctica, es soportado en todos lados.
Recapitulemos los mtodos para evitar confusiones:
| mtodo | selecciona | negativos |
|---|---|---|
slice(comienzo, final) |
desde comienzo hasta final (sin incluir final) |
permite negativos |
substring(comienzo, final) |
entre comienzo y final (no incluye final) |
valores negativos significan 0 |
substr(comienzo, largo) |
desde comienzo toma largo caracteres |
permite negativos comienzo |
Todos son capaces de hacer el trabajo. Formalmente, substr tiene una pequea desventaja: no es descrito en la especificacin central de JavaScript, sino en el anexo B, el cual cubre caractersticas slo de navegadores, que existen principalmente por razones histricas. Por lo que entornos sin navegador pueden fallar en compatibilidad. Pero en la prctica, funciona en todos lados.
De las otras dos variantes, slice es algo ms flexible, permite argumentos negativos y es ms corta.
Entones, es suficiente recordar nicamente slice.
Comparando strings
Como aprendimos en el captulo Comparaciones, los strings son comparados carcter por carcter en orden alfabtico.
Aunque existen algunas singularidades.
-
Una letra minscula es siempre mayor que una mayscula:
alert('a' > 'Z'); // true -
Las letras con marcas diacrticas estn fuera de orden:
alert('sterreich' > 'Zealand'); // trueEsto puede conducir a resultados extraos si ordenamos los nombres de estos pases. Usualmente, se esperara que
Zealandapareciera despus desterreichen la lista.
Para entender lo que pasa, debemos tener en cuenta que los strings en JavaScript son codificados usando UTF-16. Esto significa: cada carcter tiene un cdigo numrico correspondiente.
Existen mtodos especiales que permiten obtener el carcter para el cdigo y viceversa.
str.codePointAt(pos)-
Devuelve un nmero decimal que representa el cdigo de carcter en la posicin
pos:// maysculas y minsculas tienen cdigos diferentes alert( "Z".codePointAt(0) ); // 90 alert( "z".codePointAt(0) ); // 122 alert( "z".codePointAt(0).toString(16) ); // 7a (si necesitamos el valor del cdigo en hexadecimal) String.fromCodePoint(code)-
Crea un carcter por su
cdigonumrico:alert( String.fromCodePoint(90) ); // Z alert( String.fromCodePoint(0x5a) ); // Z (tambin podemos usar un valor hexa como argumento)
Ahora veamos los caracteres con cdigos 65..220 (el alfabeto latino y algo ms) transformndolos a string:
let str = '';
for (let i = 65; i <= 220; i++) {
str += String.fromCodePoint(i);
}
alert( str );
// salida:
// ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
//
Lo ves? Caracteres en maysculas van primero, luego unos cuantos caracteres especiales, luego las minsculas.
Ahora se vuelve obvio por qu a > Z.
Los caracteres son comparados por su cdigo numrico. Cdigo mayor significa que el carcter es mayor. El cdigo para a (97) es mayor que el cdigo para Z (90).
- Todas las letras minsculas van despus de las maysculas ya que sus cdigos son mayores.
- Algunas letras como
se mantienen apartadas del alfabeto principal. Aqu el cdigo es mayor que cualquiera desdeahastaz.
Comparaciones correctas
El algoritmo correcto para realizar comparaciones de strings es ms complejo de lo que parece, debido a que los alfabetos son diferentes para diferentes lenguajes. Una letra que se ve igual en dos alfabetos distintos, pueden tener distintas posiciones.
Por lo que el navegador necesita saber el lenguaje para comparar.
Por suerte, todos los navegadores modernos mantienen la internacionalizacin del estndar ECMA 402.
Este provee un mtodo especial para comparar strings en distintos lenguajes, siguiendo sus reglas.
El llamado a str.localeCompare(str2) devuelve un entero indicando si str es menor, igual o mayor que str2 de acuerdo a las reglas del lenguaje:
- Retorna
1sistres mayor questr2. - Retorna
-1sistres menor questr2. - Retorna
0si son equivalentes.
Por ejemplo:
alert('sterreich'.localeCompare('Zealand')); // -1
Este mtodo tiene dos argumentos adicionales especificados en la documentacin, la cual le permite especificar el lenguaje (por defecto lo toma del entorno) y configura reglas adicionales como sensibilidad a las maysculas y minsculas, o si "a" y "a" deben ser tratadas como iguales, etc.
Resumen
- Existen 3 tipos de entrecomillado. Los backticks permiten que una cadena abarque varias lneas e insertar expresiones
${}. - Podemos usar caracteres especiales como el salto de lnea
\n. - Para obtener un carcter, usa:
[]o el mtodoat. - Para obtener un substring, usa:
sliceosubstring. - Para convertir un string en minsculas/maysculas, usa:
toLowerCase/toUpperCase. - Para buscar un substring, usa:
indexOf, o para chequeos simplesincludes/startsWith/endsWith. - Para comparar strings de acuerdo al idioma, usa:
localeCompare, de otra manera sern comparados por sus cdigos de carcter.
Existen otros mtodos tiles:
str.trim()remueve (recorta) espacios desde el comienzo y final de un string.str.repeat(n)repite el stringnveces.- y ms. Puedes ver el manual para ms detalles.
Los strings tambin tienen mtodos para buscar/reemplazar que usan expresiones regulares. Este es un tema muy amplio, por ello es explicado en una seccin separada del tutorial Expresiones Regulares.
Adems, es importante saber que los strings estn basados en la codificacin Unicode, y se presentan algunas complicaciones en las comparaciones de string. Hay ms acerca de Unicode en el captulo Unicode, String internals.
Comentarios
<code>, para varias lneas envolverlas en la etiqueta<pre>, para ms de 10 lneas utilice una entorno controlado (sandbox) (plnkr, jsbin, codepen)