Hace un tiempo os traje una publicación en la que os mostraba algunas cosas curiosas que tiene el lenguaje JavaScript. Lo puedes ver en este enlace: «Rarezas y cosas que no sabías de JavaScript«.
Expresiones regulares
Vamos a comenzar hablando de algunos secretos en las expresiones regulares. Este en concreto, apareció en la versión 1.3.
El uso que se le suele dar a la función «replace()» suele ser algo de este estilo: alert('10 13 21 48 52'.replace(/d+/g, '*')); //reemplaza todos los números por *
. Se trata de una forma simple de usarlo, pero ¿qué pasaría si quisiéramos tener más control? ¿Qué pasaría si, por ejemplo, quisiéramos reemplazar solo los números menores de 20? Necesitamos saltar a una función de devolución de llamada para evaluar cada coincidencia.
alert('10 13 21 48 52'.replace(/d+/g, function(match) { return parseInt(match) 20 ? '*' : match;}));
Para cada coincidencia realizada, JavaScript llama a nuestra función y pasa la coincidencia a nuestro argumento de coincidencia. Luego, devolvemos el asterisco (si el número coincidente es inferior a 20) o la coincidencia misma (es decir, no debería realizarse ninguna coincidencia).
Otra función que es desconocida por mucha gente es la función «test()». Seguro que has usado expresiones regulares para buscar coincidencias en cadenas de texto. Y seguro que las funciones que has utilizado son «match» y «replace«, ya que solo con esas dos ya resuelves una gran parte de tus problemas. La función «test» funciona igual que «match» pero en lugar de devolver la coincidencia, devuelve true o false en caso de que el patrón coincida o no lo haga. Ejemplo: /w{3,}/.test('Hello')
–> devuelve ‘true’.
Finalmente destacar la función «RegExp» que permite utilizar expresiones regulares de forma dinámica, es decir, permite pasar una variable que contenga la expresión regular, en lugar de tener que declararla con la forma corta (entre barras diagonales, como en el ejemplo anterior).
function findWord(word, string) { var instancesOfWord = string.match(new RegExp('b'+word+'b', 'ig')); alert(instancesOfWord);}findWord('car', 'Carl went to buy a car but had forgotten his credit card.');
Debido a que se especifican como cadenas, no mediante sintaxis de barra diagonal, podemos usar variables para crear el patrón. Sin embargo, esto también significa que debemos aplicar doble escape a cualquier carácter especial, como hicimos con el carácter de límite de palabra.
Misceláneas
Este problema no es exclusivo de JavaScript; de hecho, es una peculiaridad común en la informática que afecta a muchos lenguajes. La salida que obtienes al hacer esto 0.1 + 0.2
es 0.30000000000000004
.
Esto está relacionado con lo que se conoce como la precisión de las máquinas. Cuando JavaScript intenta ejecutar la operación anterior, convierte los valores a sus equivalentes binarios.
Aquí es donde surge el problema: el valor 0.1
no es exactamente 0.1
, sino una aproximación binaria cercana. En resumen, tan pronto como ingresas estos valores, ya están destinados a perder algo de precisión. Quizás esperabas dos decimales simples, pero lo que obtienes es el resultado de una aritmética binaria de punto flotante. Es como intentar traducir tu texto al ruso pero recibirlo en bielorruso: parecidos, pero no iguales.
Hay más aspectos técnicos aquí, pero son complejos y exceden el alcance de este artículo.
Las soluciones a este problema son un tema frecuente en los foros de desarrollo. La elección de una solución depende del tipo de cálculos que estés realizando. Aunque no entraremos en detalle sobre los pros y contras de cada método, las opciones comunes incluyen:
- Convertir a enteros y hacer los cálculos antes de volver a convertir a decimales.
- Modificar la lógica para permitir un rango de valores en lugar de un resultado exacto.
Es decir, en lugar de hacer:
var num1 = 0.1, num2 = 0.2, shouldEqual = 0.3;
alert(num1 + num2 == shouldEqual); // false
Podías hacer:
alert(num1 + num2 > shouldEqual - 0.001 && num1 + num2 < shouldEqual + 0.001); // true
En esencia, estás verificando si la suma de 0.1 + 0.2
es aproximadamente 0.3
, dentro de un margen de error de 0.001
a cada lado. El inconveniente es que, para cálculos que requieren alta precisión, esta aproximación podría no ser adecuada.
Algo indefinido se puede definir
Vamos a terminar con un caso curioso y aparentemente inofensivo. Aunque undefined
tiene un significado especial en JavaScript y se utiliza para determinar si una variable no está definida, sorprendentemente, no es una palabra reservada. Esto significa que puedes reasignarla, lo cual puede llevar a comportamientos inesperados. Por ejemplo:
var someVar;
alert(someVar == undefined); // evalúa como true
Hasta aquí todo parece normal. Sin embargo, considera lo siguiente:
undefined = "¡Ya no soy undefined!";
var someVar;
alert(someVar == undefined); // evalúa como false