martes, 25 de junio de 2013

JavaScript: Borrar elementos de un array

El operador 'delete' no borra el elemento

Si utilizamos delete con un elemento de un array:

var myArray = ["A","B","C"];
delete myArray[1];  // myArray -> ["A", undefined, "C"]

El elemento no se borra del array. Sería más exacto decir que el elemento concreto se borra (queda como undefined), pero el array no se re-indexa. Sigue teniendo los mismos indices y el mismo número de elementos.

Utiliza splice()

Para eliminar el elemento debemos utilizar el método splice() que tienen todos los arrays:

myArray.splice(index, count);

index es el índice que queremos borrar y count es el número de elementos a borrar.

var myArray = ["A","B","C"];
myArray.splice(1,1);  // myArray -> ["A", "C"]

Hemos eliminado el elemento que tenía índice 1.

No confundir con slice()

El método slice() es similar pero no modifica el array sobre el que se aplica, sino que devuelve los elementos 'extraidos':

myArray.slice(start, end);

start es el índice en el que empezamos y end el índice hasta el que 'extraemos' elementos. El elemento en la posición end no estará incluido.

var myArray = ["A","B","C"];
var result  = myArray.slice(1,2);  // myArray -> ["A", "B", "C"]
                                   // result  -> ["B"]

El array original no se modifica pero se devuelven los elementos desde el 1 hasta el 2 (no incluido).

Resumen

  • delete myArray[1] -> ["A", undefined, "C"]
  • myArray.splice(1,1) -> ["A", "C"]
  • myArray.slice(1,2) no modifica el array pero devuelve ["B"]

viernes, 21 de junio de 2013

Rollovers, precarga de imágenes y sprites

Cuando necesitamos mostrar imágenes que no están en la página inicial, la petición de la imagen y la carga tardan un tiempo que puede ser apreciable por el usuario. En algunas ocasiones este tiempo de espera no es aceptable. Por ejemplo cuando un botón cambia al pulsarlo. No es lógico que el usuario haga click y el botón reaccione medio segundo despues.

Hay varias técnicas para evitar esta espera:

  • Precargar las imágenes y cambiarlas dinámicamente
  • Crear varias imagenes dentro del elemento y ocultar/mostrar según sea necesario
  • Utilizar sprites para cargar todas las imágenes juntas

El problema

Como ejemplo vamos a trabajar con un botón tipo interruptor:

Tenemos dos imágenes separadas para representar los estados on y off del botón:

Queremos que cuando el usuario pulse el botón, la imagen cambie instantáneamente. En el evento click podemos asignar una función que sustituya una imagen por otra:

//html
//JavaScript (function () { //save img DOM element for quick access var $img = $("#switch img"); //define click handler to switch images $("#switch").click( function () { if ( $img.hasClass("on") ) { $img.attr("src", "images/switchOff.png"); } else { $img.attr("src", "images/switchOn.png"); } $img.toggleClass("on"); }); })();

En el HTML sólo creamos el elemento con la imagen inicial, con el interruptor a on (switchOn.png). Utilizaremos una clase "on" para averiguar facilmente el estado de nuestro botón.

Con el JavaScript simplemente asignamos una función que se lanzará en el evento click. La función averigua el estado actual del botón leyendo la clase, cambia la imagen a la correspondiente al estado contrario y cambia la clase para indicar el nuevo estado.

Esto funciona pero presenta un problema. La primera vez que se pulsa el botón la imagen no está cargada. Perdir y cargar la imagen lleva un tiempo y hace que la respuesta no sea inmediata. El click habrá terminado y el botón aún no ha cambiado. Cuanto peor sea la conexión o el servidor, mayor será el retraso y peor la experiencia del usuario.

La segunda vez que se pulse el botón la respuesta sí será inmediata, porque la imagen ya está cargada. Está en la caché del navegador. La solución entonces, para tener una buena respuesta inicial, sería pre-cachear la segunda imagen al cargar la página.

Precarga de imágenes

La idea es cargar las imágenes en la caché del navegador antes de que las necesitemos. Cuando las mostremos aparecerán inmediatamente.

Para pre-cachear la imagen podemos crear un objeto image en memoria:

var imgOff = new Image();
imgOff.src = "images/switchOff.png";

La primera línea crea un objeto Image y la segunda le asigna el path o URL de la imagen para que la carge. Opcionalmente podemos pasarle la altura y la anchura al constructor.

Con esto ya tendríamos la imagen disponible en la caché del navegador y su uso sería inmediato. Sólo tenemos que asignar imgOff.src al src de la imagen que estamos cambiando:

      $("#switch img").attr("src", imgOff.src);

El código completo precargando la imagen sería:

(function () {

  //preload second image
  var imgOff = new Image();
  imgOff.src = "images/switchOff.png";

  //save img DOM element for quick access
  var $img = $("#switch img");

  //define click handler to switch images
  $("#switch").click( function () { 
    if ( $img.hasClass("on") ) {
      $img.attr("src", imgOff.src);    // <-- assigned from our object
    }
    else {
      $img.attr("src", "images/switchOn.png");
    }
    $img.toggleClass("on");
  });

 })(); 

new Image() vs createElement("img")

En el ejemplo que hemos visto antes creábamos un objeto Image para cargar la imagen. En realidad este objeto es una reliquia del DOM Level 0, que es como se conoce al DOM previo a la primera estandarización (DOM level 1). Todos los navegadores proporcionan el API definido inicialmente en este DOM pre-estandar por compatibilidad.

La forma 'actual' de hacer la precarga sería:

  var imgOff = document.createElement("img");
  imgOff.src = "images/switchOff.png";

Las dos formas son equivalentes y son correctas. La que más se utiliza y la que me parece más elegante es new Image(), aunque sea parte de un API antiguo.

Precargando varias imágenes

Si tenemos varias imagenes para cachear podemos hacer:

var images = "one.png, two.png, three.png".split(",");
var tempImg = [];

for(var i=0; i < images.length; i++) {
    tempImg[i] = new Image();
    tempImg[i].src = images[x]
}

Ocultar un elemento y mostrar otro

Para hacer rollovers con sólo dos imágenes, como el ejemplo que estamos tratando, podemos crear el elemento con las dos imágenes inicialmente. Una estará oculta (switchOff.png) y la otra visible (switchOn.png). Al hacer click ocultamos On y mostramos Off. Al estar las dos imagenes en el documento desde el principio, las dos se cargarán en el inicio.

Para implementarlo crearemos una clase "hidden" con display: none. Colocaremos esa clase a la imagen que queramos ocultar:


//html
//CSS .hidden { display: none; } //JavaScript (function(){ //store all img elements for quick access var $imgs = $("#switch img"); $("#switch").click( function () { for (var i = 0; i < $imgs.length; i++) { $imgs.eq(i).toggleClass("hidden"); } }); })();

Ahora la variable $imgs contiene un array con dos imágenes. En el click recorremos las dos imagenes y cambiamos la clase 'hidden' en cada una de ellas. A la imagen que no la tiene se la ponemos para ocultarla y a la que la tenía se la quitamos para mostrarla.

La instrucción $imgs.eq(i).toggleClass("hidden"); selecciona, del array de imágenes, la que esté en el index 'i' y permuta la clase 'hidden' ( si la tiene se la quita y si no la tiene se la añade).

Utilizando Sprites

Un sprite son varias imágenes juntas en un solo fichero. Se utiliza mucho para imágenes pequeñas, tipo iconos, botones o símbolos del UI.

Este es un sprite que utiliza Google:

Es una sóla imagen .png que contiene todos los iconos que utilizan en su interfaz. Sólo se hace una petición HTTP y todas las imágenes quedan ya disponibles en la caché.

En nuestro ejemplo tenemos que construir un sprite con las dos imagenes que utilizamos:

Cuando el navegador cargue la imagen para la posición inicial del botón, ya tenemos cargada la otra (es la misma imagen). Evidentemente necesitamos alguna manera de mostrar sólo la parte que nos interesa.

Cuando la imagen que necesitamos forma parte de un sprite no podemos utilizarla en una etiqueta <img>, tenemos que colocarla siempre como imagen de fondo de un elemento (background-image). Es muy importante conocer la posición exacta de cada imagen en la composición total, así como su anchura y altura porque jugaremos con la propiedad CSS background-position para colocarla.

Como ahora el <div> del botón está vacío (no contiene una <img>), tenemos que definir su anchura y su altura en CSS para que se vea justo la parte de la imagen de fondo que nos interesa:


//html
//CSS #switch { width: 70px; height: 35px; background-image: url("images/switch_Sprite.png"); background-position: 0 35px; }

Utilizamos background-position para definir la coordenada (x, y) desde la que queremos que se muestre la imagen. En nuestro caso la imagen en off empieza en (x=0, y=0) y la imagen en on en (x=0, y=35px). Por lo que inicialmente mostraremos la imagen en background-position: 0 35px. Al hacer click cambiaremos a background-position: 0 0. A partir de la posición especificada se mostrará lo que quepa en los 70x35 pixels que hemos definido como contenedor.

En la siguiente imagen hemos colocado un borde naranja alrededor del *div* para apreciar mejor la zona donde se coloca el *background*:

El event handler que tenemos que crear en JavaScript para hacer el cambio sería:

(function(){

  var state = "on";

  $("#switch").click( function () { 
    if ( state === "on" ) {
       $("#switch").css("background-position","0 0");
       state = "off";
    }
    else {
       $("#switch").css("background-position","0 35px");
       state = "on";
    }
  });
})();

Cuando hay un click cambiamos la posición del background para que se vea el botón contrario:

Hay que tener cuidado al definir la anchura y altura del div porque si lo hacemos mal se verán más imagenes del sprite. En el siguiente CSS aumentamos la altura:

//demasiada altura, se ve el segundo botón  
#switch3 {
  border: 1px solid orange;
  width: 70px;
  height: 50px;
  background-image: url("images/switch_Sprite.png");
  background-position: 0 0;
}

El resultado es:

Al utilizar sprites es muy importante ajustar el tamaño del contenedor para que quepa justo la imagen que queremos presentar.

martes, 18 de junio de 2013

Funciones parseInt(), parseFloat(), Number() y conversion implícita

Siguiendo con el tema de la conversión de tipos que vimos en el post anterior, vamos a ver ahora en detalle las posibilidades que ofrece el lenguaje para convertir otros tipos en números.

Veremos parseInt(), parseFloat(), Number() y las formas de conversión rápida (conversión implícita), comentando las diferencias entre ellas.

parseInt() y parseFloat()

parseInt() y parseFloat() son funciones creadas para parsear un string y devolver un número si es posible. Los espacios iniciales y finales se ignoran.

JavaScript analiza la cadena para extraer las cifras que encuentre al principio. Estas cifras al principio del string son las que se transforman a tipo numérico. Cuando se encuentra el primer carácter no numerico se ignora el resto de la cadena. Si el primer carácter encontrado no es convertible a número, el resultado será NaN (Not a Number).

Cuando el valor no es un string, JavaScript hace primero una conversión implícita a string. Esta conversión, en el caso de objetos, se hace llamando al método toString(). Podemos reescribir este método para algunos objetos si nos interesa asegurarnos de que devuelvan un string convertible a número.

parseInt(string, radix) - Conversión de string a entero

radix es opcional y representa la base en la que estamos trabajando. Normalmente trabajaremos en base decimal y este será el valor tomado por defecto en los navegadores modernos (como se define en ECMAScript 5). En navegadores antiguos (IE7, IE8) los número que empiezan por "0" se consideran en base octal por defecto, a no ser que se indique explícitamente el radix 10.

El prefijo 0x indica que el número está en hexadecimal aunque no se incluya radix 16.

Algunos ejemplos:

    parseInt("10");         // 10
    parseInt("10.8");       // 10
    parseInt("10 22");      // 10 
    parseInt(" 14 ");       // 14
    parseInt("20 dias");    // 20
    parseInt("Hace 20 dias"); // NaN
    parseInt("44aa33bb");   // 44
    parseInt("3.14");       // 3
    parseInt("314e-2");     // 314
    parseInt("");           // NaN  ->  ¡¡el string vacio se convierte a NaN!!
    parseInt(null);         // NaN

    parseInt("10",10);      // 10
    parseInt("010");        // 10  ¡¡ * 8 en navegadores antiguos *  !!
    parseInt("10",8);       // 8
    parseInt("0x10");       // 16   0x indica que el número es hexadecimal
    parseInt("10",16);      //16

paseFloat(string) - Conversión de string a número en coma flotante

En este caso no existe radix. El número siempre se interpreta como decimal, independientemente de cualquier prefijo que le pongamos (0 para octal ó 0x para hexadecimal).

La diferencia con el anterior, ademas de admitir decimales, es que los números en coma flotante admiten la notación exponencial, del tipo "314e-2" o "0.0314e+2".
Si la función encuentra un carácter que no sea un número (0-9), un signo (+ o -), un punto decimal o un exponente, ignorará todos los caracteres que vengan a continuación.

Algunos ejemplos;

    parseFloat("3.14");      // 3.14
    parseFloat("314e-2");    // 3.14
    parseFloat("0.0314E+2"); // 3.14
    parseFloat("3.14dieciseis"); // 3.14
    parseFloat("A3.14");     // NaN
    parseFloat("tres");      // NaN 
    parseFloat("e-2");       // NaN
    parseFloat("0x10");      // 0     ->  No admite el prefijo 0x para indicar 'hexadecimal' 
    parseFloat("");          // NaN   ->  ¡¡el string vacio se convierte a NaN!!
    parseFloat(null);        // NaN

Esta función es muy util para convertir valores en pixels o puntos, de CSS, en valores numéricos:

parseFloat("5px");      // 5

Number()

A diferencia de los dos métodos anteriores, éste y el siguiente (conversión implícita) son especificamente para conversión de tipos. parseInt() y parseFloat() son para extraer un número de un string.

Number() es un constructor para crear objetos de tipo Number, pero cuando se utiliza sin el new funciona como un conversor a tipo númerico.

  • como constructor: var myNumber = new Number(14);
  • como método: var myNumber = Number("14");

El segundo uso, sin new, es el que nos interesa para este tema.

Puede utilizarse para números enteros o decimales y acepta también la notación exponencial.

    
Number("12");        // 12
Number("3.14");      // 3.14
Number("314e-2");    // 3.14
Number("0.0314E+2"); // 3.14
Number("e-2");       // NaN
Number('0x10');      // 16   admite el prefijo 0x para indicar 'hexadecimal'

Ignora los espacios al principio y al final, pero, diferencia de los métodos anteriores, cuando un string contiene caracteres no convertibles a números el resultado siempre es NaN, no trata de 'extraer' la parte numérica.

    
Number("12");             // 12
Number("   12 ");         // 12
Number("20 dias");        // NaN
Number("Hace 20 dias");   // NaN
Number("44aa33bb");       // NaN
Number("");               // 0    ->  ¡¡el string vacio se convierte a 0!!  
Number("       ");        // 0
Number(null);             // 0

Con Number() podemos convertir booleans en números, false siempre se convierte en 0 y true en 1.

Number(true);    // 1
Number(false);   // 0

//también podemos incluir una expresión con resultado boolean
Number( (1<2) );    // 1
Number( (1===2) );  // 0

Cuando lo utilizamos con un objeto Date, devuelve los milisegundos en Unix time (desde el 1 de enero de 1970, UTC):

Number( new Date() );    // 1371220353601

En general, cuando le pasamos un objeto llamará a .valueOf() y, si no es posible, a .toString().

Conversión implicita '+'

La conversión implícita es una forma de conversión rápida a número. Podemos utilizar cualquier operación que fuerce al intérprete a realizar una conversión implícita de tipos pero que no varíe el operando:

var myNumberValue = "8" - 0; // number 8
var myNumberValue = "8" * 1; // number 8
var myNumberValue = "8" / 1; // number 8
var myNumberValue = +"8";    // number 8

La forma más utilizada por su simplicidad es +var. El operador unitario + no cambia el valor de var pero lo convierte a número. No confundir con ++var que sí cambia el valor, sumándole uno.

Este tipo de conversión, igual que Number(), devuelve NaN si el string contiene caracteres no numéricos.

Como veremos en los siguientes ejemplos, esta forma de conversión es equivalente a Number() y devuelve los mismos resultados:

    
+"12";             // 12
+"3.14";           // 3.14
+"314e-2";         // 3.14
+"0.0314E+2";      // 3.14
+"e-2";            // NaN
+"0x10";           // 16   admite el prefijo 0x para indicar 'hexadecimal'

+"   12 ";         // 12
+"20 dias";        // NaN
+"Hace 20 dias";   // NaN
+"44aa33bb";       // NaN
+"";               // 0    ->  ¡¡el string vacio se convierte a 0!!  
+"       ";        // 0
+null;             // 0

//boolean 
+true;    // 1
+false;   // 0

//también podemos incluir una expresión con resultado boolean
+(1<2);    // 1
+(1===2);  // 0

//objetos
+( new Date() );    // 1371220353601

Conclusión

Las principales diferencias son:

  • parseInt() tiene un parámetro extra para indicar la base del número (radix).
  • parseFloat() no admite radix. Todos los números se consideran en base decimal.
  • parseInt(), Number() y '+' interpretan el prefijo '0x' como número hexadecimal, parseFloat() no.
  • parseInt() y parseFloat() pueden extraer un número al principio de un string.
  • Si el string contiene caracteres no numéricos, Number() y '+' no lo convierten, devuelven NaN.
  • Cuando el argumento es un objeto, parseInt() y parseFloat() llamarán al método .toString() antes de analizar la cadena. Number() y +var llamarán primero a .valueOf() y despues a .toString() si es necesario.
  • parseInt() no entiende la notación exponencial, todos los demás si.
  • parseInt() y parseFloat() convierten el string vacio en NaN.
  • Number() y '+' convierten el string vacio en 0.
  • parseInt() y parseFloat() de un boolean es NaN.
  • Number() y '+' de un boolean devuelven 0 para false y 1 para true.

Fuentes:

StackOverflow: Which is better, number(x) or parseFloat(x)?
StackOverflow: Side effects converting strings ...

lunes, 10 de junio de 2013

Conversión rápida de tipos en JavaScript

JavaScript puede convertir dinámicamente el valor de una variable de un tipo a otro, dependiendo del contexto. Por ejemplo, si hacemos la siguiente multiplicación:


var result = "4" * 2  // resultado 8 

En intérprete convertirá implícitamente el string "4" en un número porque entiende, por el contexto, que queremos multiplicar dos números.

A veces la conversión implícita es un problema y el resultado no es lo que queremos:


var result = "4" + 8  //resultado "48"

En este caso JavaScript considera que queremos concatenar dos strings. Convierte 8 en un string y devuelve "48". Sólo si los dos operandos son números se realizará la suma, si cualquiera de ellos es un string se concatenan.

Para todos los casos en los que la conversión automática puede darnos problemas, lo mejor es que nosotros forcemos la conversión a los tipos apropiados.

Utilizando constructores

Los tipos básicos ( boolean, string, number .... ) tienen un constructor asociado (por si queremos crearlos como objetos en vez de tipos primitivos). Podemos utilizar el constructor como una función para realizar la conversión explícita:


Number("4");     // 4
Number("Hola")   // NaN ( Not a Number)
Number("3 Hola") // NaN
Number(1e3)      // 1000 ( "e" significa exponente)

String(4);       // "4"
String(0.7);     // "0.7"

Boolean(4);      // true
Boolean(0);      // false
Boolean([1,2,3]) // true

Existen también las funciones parseInt() y parseFloat() para hacer la conversión de strings a números. Estos métodos los veremos en detalle en otra entrada.

Forma rápida de conversión de tipos

Doble negación para convertir a boolean

var myBooleanValue = !!8; // true

Concatenar un string vacío para convertir a string

var myStringValue = "" + 8; // "8"

'+' para convertir a número

Los strings son siempre convertidos en números automáticamente si actuan como operandos de una expresión matemática ( excepto para la suma, que también concatena textos ). Sabiendo esto, una forma rápida de convertir un string en un número es incluirlo en alguna operación que no modifique su valor, como restarle 0 o multiplicarlo por 1:


var myNumberValue = "8" - 0; // number 8
var myNumberValue = "8" * 1; // number 8
var myNumberValue = "8" / 1; // number 8

Sin embargo, lo que más se utiliza por su simplicidad es el "+" como operador unitario. En este caso el comportamiento de la suma sí está perfectamente definido y claro y sabemos con toda seguridad que convierte un string en un número:


var myString = "8"

var myNumberValue =  +myString  // number 8
var myNumberValue =  +"20"      // number 20

Esta conversión es la más rápida pero resulta en una notación un poco confusa. Puede confundirse con un intento erróneo de pre-incremento de una variable ( ++myVariable ). A veces se coloca entre paréntesis para intentar dejar más clara la conversión:


var myString = "8"

var myNumberValue =  (+myString)  // number 8

La conversión utilizando Number() es la más lenta de todas.

Resumiendo las opciones de conversión rápida:


//to boolean
var myBooleanValue = !!20; // true

//to string
var myStringValue = "" + 20; // "20"

//to number
var myNumberValue =  +"20"      // number 20



Fuentes:
Strings to Numbers
JavaScript, The Definitive Guide. Type Conversions
Type Conversion

viernes, 7 de junio de 2013

Detectar si una variable es un array en JavaScript

Para detectar el tipo de una variable normalmente utilizamos el operador typeof:

typeof "Hello";  //returns "string"
typeof 3;  //returns "number"

Este operador funciona bien para los tipos boolean, number, string y undefined, pero cuando la variable es un objeto ( por ejemplo un Array ) devuelve siempre "object":


typeof [];  //returns "object"
typeof {};  //returns "object"
typeof new Date(); //returns "object" 

El operador instanceof

Para conocer el tipo de objeto utilizamos el operador instanceof de la siguiente forma:

variable instanceof constructor;

De esta forma podemos preguntar si una variable es una instancia de Array, de Date, de algún constructor propio, etc.


var myArray = [];

myArray instanceof Array;  // true
myArray instanceof Number; // false
myArray instanceof String; // false
myArray instanceof Object; // true

En la última línea vemos que para un array, el operador también devuelve true en la comparación con Object. Esto es porque un array es una instancia de 'Array' y tambien de 'Object' porque el constructor Array hereda de Object.

Problemas si utilizamos multiples frames

La solución que hemos visto nos dará problemas si manejamos arrays creados en frames diferentes. Cada frame en una página tiene un entorno DOM propio y cada uno tendrá su propia clase 'Array' y 'Object'. Si intentamos hacer la comprobación con un array creado en otro frame:


arrayFromAnotherFrame instanceof Array;  // false

El resultado es false porque no es una instancia del constructor 'Array' de este frame.

El estándar de facto para detectarlo correctamente en todos los casos, propuesto por Juriy Zaytsev (Kangax), consiste en utilizar toString sobre el objeto y comprobar si devuelve "[object Array]":


function isArray(value) {
     return Object.prototype.toString.call(value) === "[object Array]";
}

ECMAScript 5 introduce Array.isArray()

La nueva versión de JavaScript introduce un método específico para detectar si un valor es un array:


var myArray = [];

Array.isArray( myArray );  // true

Soportado en IE9+, Firefox 4+, Chrome, Safari 5+ and Opera 10.5+.

Una buena solución si tenemos que dar soporte a navegadores antiguos es crear el método isArray si no existe:

if(!Array.isArray) {
  Array.isArray = function (value) {
    return Object.prototype.toString.call(value) === "[object Array]";
  };
}

Fuentes:
Perfection Kills: instanceof considered harmful

miércoles, 5 de junio de 2013

Tutorial de expresiones regulares en JavaScript

¿Qué son las expresiones regulares?

Una expresión regular es una forma de definir una 'plantilla', con una sintaxis propia, para localizar un patrón dentro de un texto. El ejemplo más sencillo sería definir una palabra como plantilla:

Expresión regular: /casa/

Encontraría la palabra casa en el texto:

La casa de la montaña.

La utilidad real se muestra cuando utilizamos caracteres especiales para crear patrones de busqueda mucho más complejos, del tipo:

Expresión regular: /\W*\s(\S)*$/

Parece mucho más complicado de lo que es en realidad. Sólo hay que aprender unas reglas básicas y unos cuantos caracteres especiales.

JavaScript tiene dos notaciones

En JavaScript hay dos formas de crear una expresión regular: mediante la notación literal o utilizando el constructor RegExp():


//notación literal
var myRegExp = /casa/g; /* /reg. expression/flags(g|i|m) */

//Usando el constructor RegExp
var myRegExp = new RegExp('casa','g'); /* RegExp('expression','flags(g|i|m)'); */

Las dos son equivalentes. La primera se compila cuando el script se carga y es más rápida. La segunda se compila en tiempo de ejecución por lo que es la única opción si la expresión contiene variables que tienen que ser interpoladas. En este tutorial utilizaremos la notación literal por simplicidad. Todas las explicaciones son válidas también para el constructor.

Para entender mejor el funcionamiento de las expresiones regulares conviene pensar en una busqueda caracter a caracter. Si tenemos el patrón 'casa', lo que estamos buscando no es la palabra casa, sino una 'c' seguida de una 'a' seguida de una 's', etc. Es mucho más facil entender el uso de los caracteres especiales de esta manera.

Por ejemplo, si sabemos que \s representa un espacio en blanco y que ? significa que el caracter anterior es opcional, ¿ que estamos seleccionando en la siguiente expresión regular?:

var myRegex = /\sglobos?/;

Estamos buscando un espacio en blanco, seguido de una 'g', de una 'l', de una 'o', de una 'b', de una 'o' y de una 's' que es opcional, puede aparecer o no. Por tanto seleccionaremos:

Un globo, dos globos, tres Globos.

Podemos ver que sólo ha seleccionado el primer 'globo'. Por defecto la búsqueda termina cuando se encuentra la primera coincidencia. Si queremos que siga buscando tenemos que utilizar el flag g.


Flags (g, i, m)

Los flags modifican el comportamiento por defecto de la búsqueda. Aparecen justo al lado de la barra que marca el final de la expresión regular y pueden ser:

  • g (global). Se busca siempre en el texto completo en vez de detenerse cuando encuentra la primera coincidencia con el patrón.
  • i (ignora Mayusculas). No diferencia mayúsculas y minúsculas.
  • m (multilínea). El texto incluye saltos de línea. ^ y $ se aplican a 'comienzo de línea' y 'final de línea', en vez de 'comienzo de texto' y 'final de texto'.

Si en la cadena de nuestro ejemplo queremos seleccionar también otras ocurrencias de la palabra 'globo', tenemos que utilizar:

var myRegex = /globo/g;
Un globo, dos globos, tres Globos.

La expresión encontraría dos coincidencias. El tercer 'Globo' no coincide con el patrón porque la G es mayúscula. Si añadimos el modificador i sí se seleccionará:

var myRegex = /globo/gi;
Un globo, dos globos, tres Globos.

La segunda y tercera aparición están en plural. Sería bueno que nuestra expresión regular capturara tanto globo como globos. Como ya vimos antes, podemos utilizar la ? para indicar que el carácter anterior es opcional:

var myRegex = /globos?/gi;
Un globo, dos globos, tres Globos.

?, + , *   Cuantificadores sobre el carácter anterior

Con estos tres caracteres extraños podemos indicar que el carácter inmediatamente anterior puede ser opcional, o que tiene que aparecer como mínimo una vez o que puede aparecer muchas veces.

?El carácter anterior es opcional. Aparece 0 ó 1 veces.
+El carácter anterior aparece una vez o más.
*El carácter anterior puede no aparecer, aparecer una vez o aparecer repetido muchas veces (aparece 0 o más veces).

Si tenemos el texto:

"Tengo un gloooooobo grande y tres globos pequeños."

¿Como podemos hacer para capturar los dos globos?. La expresión anterior no nos sirve porque la primera aparición quedaría fuera.

Necesitamos una expresión regular que nos seleccione cualquier patrón con una 'g' seguida de una 'l', seguida de una o más 'o', una 'b', una 'l', una 'o' y una 's' opcional.

var myRegex = /glo+bos?/gi;
Tengo un gloooooobo grande y tres globos pequeños.

En este caso, si ponemos * en vez de + también funcionaría. La diferencia es que el asterisco permite que el carácter no aparezca ( cero o más veces). Por lo tanto también seleccionaría glbo. Con + obligamos a que el carácter aparezca al menos una vez.

Para seleccionar todas las palabras en el siguiente texto:

"bo, boooo, b."

sólo nos sirve el *:

var myRegex = /bo*/g;
bo, boooo, b.

'.' Un comodín para un solo car.cter

El punto '.' es un comodín que puede sustituir a cualquier carácter excepto a un salto de línea.

La siguiente expresión:

var myRegex = /gat./gi;

capturará 'gat' seguido de cualquier carácter:

Gato, gata, gamo, gatuno, gatitos, gatoooooos, gatk, gallego, gat , gat.

En las dos últimas coincidencias podemos ver que también captura el espacio en blanco y el punto final.

{n,m} Cuantificadores más concretos

Con {n,m} podemos indicar exactamente el número de veces que aparece el carácter anterior o delimitarlo en un rango concreto ( por ejemplo que aparezca entre 3 y 5 veces).

{n}El carácter anterior aparece exactamente *n* veces.
{n,}El carácter anterior aparece *n* o más veces.
{n,m}El carácter anterior aparece un mínimo de *n* y un máximo de *m* veces

var myRegex = /r{2}/gi;
b, brr, brrr, brrrr.

En el último caso tenemos brrrr. Se seleccionan las dos primeras y despues las dos segundas.

var myRegex = /r{2,}/gi;
b, brr, brrr, brrrr.
var myRegex = /r{2,3}/gi;
b, brr, brrr, brrrr.

[abc] Conjuntos de caracteres

Para indicar que en un lugar de nuestra expresión puede aparecer cualquier de los caracteres de un conjunto concreto podemos utilizar [ ]. Por ejemplo, si queremos seleccionar 'sal', 'cal' y 'mal', podemos indicar que el primer caracter puede ser [scm]. Es decir, puede ser uno de los caracteres del conjunto:

var myRegex = /[scm]al/gi;
tal, sal, perro, mal, sota, cal, tal, salir, ramal.

También podemos negar el conjunto con el carácter ^, indicando que puede aparecer cualquier carácter menos los que están en el conjunto.

var myRegex = /[^scm]al/gi;
tal, sal, perro, mal, sota, cal, tal, salir, ramal.

Se puede especificar un rango mediante un guión: [0-7] indica números del 0 al 7, [a-z] indica letras de la 'a' a la 'z'

'^' y '$' Principio y final de línea

Muchas veces es necesario seleccionar una cadena sólamente si está al principio o al final de un texto. Por ejemplo, es muy común eliminar los espacios en blanco que puedan aparecer al principio o al final.

Sabemos que el símbolo \s selecciona un espacio en blanco. Con la siguiente expresión seleccionariamos todos los espacios al principio de un texto:

var myRegex = /^\s+/g;

Estamos buscando un 'principio de texto' seguido de uno ó mas espacios en blanco:

   Este es mi texto.

Si queremos seleccionar los del final:

var myRegex = /\s+$/g;

Estamos buscando uno ó mas espacios en blanco justo antes de un final de texto:

Este es mi texto.   

En el apartado de flags vimos que existe uno para modificar el comportamiento de estos dos carácteres. El flag 'm', cuando está presente indica que el texto es multilínea y queremos que ^ y $ seleccionen principio y final de cada línea, en vez de principio y final de texto.

\s, \d, \D, \w, \W .... Símbolos para hacer la vida más facil

Algunos conjuntos de caracteres son de uso tan común que se han creado unos símbolos para incluirlos más fácilmente. Por ejemplo, para indicar que un carácter debe ser un número podemos escribir [0-9] o podemos utilizar \d (digit) que es mucho más sencillo. Cuando el símbolo aparece en mayúscula significa exactamente lo contrario. \D sería cualquier carácter que NO sea un número.

Los más utilizados son:

\sCualquier tipo de espacio en blanco ( espacio, tabulador, salto de línea, etc)
\STodo carácter que NO sea un espacio en blanco
\tTabulador
\wCualquier carácter alfanumérico. Equivalente a [a-zA-Z0-9_]
\WCualquier carácter NO alfanumérico
\dUn dígito. Equivale a [0-9]
\DCualquier carácter que NO sea una dígito
\b'Word Boundary'. Marca el inicio o el fin de una palabra ( similar a '^' y '$' para una línea)

Como ejemplo podemos escribir una expresión para comprobar un nombre de usuario. Suponemos que el nombre de usuario no puede contener espacios y sólo puede contener números y letras (incluimos tambien el guión bajo o subrayado). Tiene que tener un mínimo de 6 carácteres y un máximo de 12:

var myRegex = /^\w{6,12}$/g;

Sólo seleccionará un nombre de usuario cuando cumpla las condiciones. En caso contrario no seleccionará nada. Puede utilizarse en una condición de JavaScript para aceptarlo o rechazarlo.

'|' OR

La barra vertical nos permite definir varias expresiones diferentes para buscar una o otra:

var myRegex = /perro|gato/gi;
perro, loro, gato, hamster, serpiente.

Se busca la coincidencia con la expresión de la de la derecha o de la de la izquierda completas. Si queremos que el OR sólo afecte a una parte de la expresión, tenemos que encerrar esa parte entre paréntesis. Por ejemplo, si queremos buscar 'elefante' y 'elemento'

var myRegex = /ele(mento|fante)/gi;
elegante, elemento, testamento, elefante, chanante, elemental.

Tambien podemos definir más de dos opciones:

var myRegex = /ele(mento|fante|gante)/gi;
elegante, elemento, testamento, elefante, chanante, elemental.

() Creando subexpresiones en nuestra regex

Los paréntesis se utilizan para marcar una parte de la expresión sobre la que actúa algún operador. En el ejemplo anterior vimos que podemos delimitar el rango sobre el que actuaba OR para que no se aplicara a la expresión completa. También podemos extender el rango sobre el que actuan los cuantificadores.

var myRegex = /1(23)+/gi;

En la expresión anterior el '+' se aplica al grupo completo '23'.

123, 1232323, 1232222, 123333333, 12323232323.

Sin los paréntesis los cuantificadores se aplican sólo al carácter inmediatamente anterior:

var myRegex = /123+/gi;
123, 1232323, 1232222, 123333333, 12323232323.

Los paréntesis tienen también otra función: memorizan todas las coincidencias que se encuentren en el texto y pueden utilizarse despues como $1, $2, $3, etc. Sólo se memoriza y guarda (en $n) la parte de la expresión marcada con los paréntesis. Por ejemplo, en una expresión que seleccione fechas, podemos encerrar entre parentesis la parte que coge el dia y el mes y luego cambirlo de orden colocando $2-$1.

Podemos utilizar ?: para evitar que se memorize el contenido:

var myRegex = /1(?:23)+/gi;

Esto es todo por ahora. En otro tutorial veremos las funciones que tenemos disponibles para utilizar las expresiones regulares de varias formas en JavaScript.

Fuentes: MDN Regular Expressions

Regular expression enlightenment
Regexpal (regex tester)