Lo que nadie explica acerca de JavaScript, 10 rarezas, secretos y errores comunes.

Hola a todos, como podréis comprobar ando un poco pesadito con JavaScript pero es lo que me motiva últimamente. Esta vez he encontrado una entrada en la que se explican algunas de las rarezas que nos podemos encontrar en este lenguaje de programación, el artículo que he encontrado sería para personas que ya poseen ciertos conocimientos en JavaScript y quieran avanzar un poco más en el aprendizaje.

He conseguido que una de mis entradas sobre prism.js un script hecho por @leaVerou sea publicado en su mismísima web!!!

Tipos de datos y definiciones.

1.NULL ES UN OBJETO

Empecemos con la mayor rareza de Javascript, aparentemente null es un objeto siendo en si una contradicción ya que la definición de null sería la total ausencia de valor. Esta es la prueba más fehaciente:

alert(typeof null); //alerts 'object'
A pesar de esto, null no es considerado una instancia de objeto. (En caso de que no lo sepas, los valores en Javascript son instancias del objeto base que las compone. Por lo que cada número es una instancia del objeto Number, cada objeto es una instancia del objeto Object...) Esta afirmación nos devuelve a la realidad porque ya que null no posee valor a su vez no puede ser la instancia de nada, veamos este ejemplo:
alert(null instanceof Object); //evaluates to false

2.NAN ES UN NÚMERO

Si con null ya nos hemos sorprendido, tratemos de comprender el concepto NaN(not a number) siendo un número. A más a más NaN se considera que no es igual a si mismo.
alert(typeof NaN); //alerts 'Number'
alert(NaN === NaN); //evaluates false

De hecho NaN no es igual a nada. La única manera que tenemos para confirmar que NaN es alguna cosa es a través de la función isNaN().

3.UN ARRAY SIN VALORES FALSE

alert(new Array()  false); //evaluates true
Para comprender lo que ocurre tenemos que entender los conceptos de truthy and faslsy, conceptos un poco apartados de la lógica. La explicación acerca de truthy and faslsy sería: En javascript, cada valor 'no-boleano' tiene una marca boleana integrada que entra en acción cada vez que le pedimos que actúe como un boleano, por ejemplo cuando lo comparamos con un boleano. Como que las manzanas no pueden ser comparadas con peras cada vez que en javascript tenemos que comparar valores que sean de diferentes tipos de datos primero los forzamos a que tomen un valor de los tipos ya existentes en el lenguaje. False, zero, null, undefinied, strings vacíos y NaN suelen convertirse en False -no permanentemente.
var some_var = 0;  
alert(some_var  false) //evaluates true
Según el ejemplo de arriba estamos intentando comparar 0 con el boleano False. Como que este tipo de datos son incompatibles, javascript transforma la variable en su valor truthy o falsy equivalente, como he dicho más arriba 0 es falsy. Puedes haber notado que en la lista de 'falsy' no hay el caso del array vacío. Los arrays vacíos evalúan hacia true pero cuando se comparan con un boleano evalúan hacia False. Otro ejemplo para ver este caso:
var some_var = []; //empty array
alert(some_var == false); //evaluates true
if (some_var) alert('hello'); //alert runs, so some_var evaluates true
Para evitar que javascript transforme las variables o valores tendríamos que usar el operador de comparación estricta ===, el cual compara por tipo y valor. El opuesto es == ya que solo compara por el valor. Ejemplo:
var some_var = 0;  
alert(some_var  false); //evaluates true-zero is falsy  
alert(some_var = false); //evaluates false-zero is a number, not boolean
Indagar más sobre el concepto truthy o falsy

Regular Expressions

4.REPLACE() PUEDE ACEPTAR UNA FUNCIÓN

La mayoría de usos de replace() sería algo así:
alert('10 13 21 48 52'.replace(/d+/g, '*')); //replace all numbers with *
Como podrás ver estamos cambiando todos los strings por un asterisco. Pero y si queremos tenemos un mayor control? Y si queremos cambiar solo los números menores de 30? Esto lo podemos conseguir con tan solo expresiones regulares (tratamos con strings no con mates), pero necesitamos una función para evaluar cada coincidencia.
alert('10 13 21 48 52'.replace(/d+/g, function(match) {  
    return parseInt(match) < 30 ? '*' : match;
}));  
Por cada coincidencia que javascript se encuentre llamaremos a la función, pasando cada coincidencia como argumento.

5.EXPRESIONES REGULARES

Muchos desarrolladores intermedios de javascript solo piensan en match() y replace() cuando hablamos de Regular Expressions pero hay más mundo. El interés en particular es con test(), el cual trabaja como match() excepto que no retorna las coincidencias, simplemente confirma que un patron coincide con otro.
alert(/w{3,}/.test('Hello')); //alerts true
El código de encima es un patron de 3 o más caracteres alfanuméricos y como que el string 'hello' coincide con lo requerido obtenemos true. Hablemos del objeto RegExp, con el que podemos crear expresiones regulares de manera dinámica en oposición a las estáticas. La mayoría de las expresiones regulares son declaradas usando el método corto (encerradas entre \\), por lo que no podemos referenciar variables o sea que es imposible crear patrones dinámicos. Con RegExp si que podemos:
function find_word(word,string) {  
    var instances_of_word = string.match(new RegExp('\b'+word+'\b','ig')); 
    alert(instance_of_word); 
}  
find_word('car', 'Carl went to buy a car but had forgott en his credit card'); 
Arriba estamos creando un patron dinámico basado en el valor del argumento word. La función retorna el número de veces que el argumento word aparece en la cadena de strings pero como una sola palabra. En el ejemplo buscamos las coincidencias con 'car' el cual solo aparece una vez, omitimos 'Carl' y 'card'. Debido a que las RegExp se especifican como cadenas de strings y no a través de la sintaxis '\\', podemos utilizar variables en la construcción del patron. Hay que tener en cuenta que hay que doble escapar los caracteres especiales, como lo hemos hecho con nuestro carácter límite.

Funciones y ámbito

6.PODEMOS FALSEAR UN ÁMBITO

El ámbito define que variables son accesibles cuando se ejecuta algún código. El javascript independiente (el que se ejecuta fuera de una función) opera en el ámbito global del objeto window, el cual todo el mundo tiene acceso, en contrapartida las variables locales declaradas dentro de una función solo son accesibles dentro de esa función.
var animal = 'dog';   
function get_animal(adjective){alert(adjective+' '+this.animal);}  
get_animal('lovely'); //alerts 'lovely dog'
En el caso de arriba, la variable y la función están declaradas en el ámbito global (p.e window). Por eso siempre apunta al ámbito actual, en este caso window. Por lo tanto, la función busca window.animal y lo encuentra. Pero en realidad podemos hacer que nuestra función piense que se está ejecutando en un ámbito diferente. Lo hacemos usando el método call() en vez de llamar a la propia función:
var animal= 'dog';   
function get_animal(adjective){alert(adjective+' '+this.animal);};  
varmy_obj = {animal:'camel'};  
get_animal.call(my_obj, 'lovely'); //alerts 'lovely camel'
Aquí nuestra función no corre en window sino en my_obj ya que lo especificamos como primer argumento en el método call(). Esencialmente, call() pretende que nuestra función sea un método de my_obj. Tener en cuenta de que apply() hace el mismo trabajo que call(), excepto que los argumentos de la función son pasados como un array en vez de manera individual.
get_animal.apply(my_obj, ['lovely']); //function argument sent as array

7.LAS FUNCIONES SE PUEDEN EJECUTAR SOLAS

(function() {alert('hello');})(); //alerts 'hello'
La sintaxis es bastante simple: declaramos una función e inmediatamente la llamamos de la misma manera que llamamos a las otras funciones, con (). A lo mejor te preguntas el porque encapsulamos la función dentro de otra anónima para que se ejecute al momento ya que normalmente queremos que las funciones se ejecuten más tarde. Un buen uso de las funciones anónimas (self-executing functions--SEF) es enlazar el actual valor de las variables para su uso posterior dentro del código, como podrían ser las 'callback' funciones en los eventos, timeOuts e intervalos. Aquí está el problema:
var some_var = 'hello';  
setTimeout(function(){alert(some_var);},1000);  
var some_var = 'goodbye';
El alert que usamos en el timeout devuelve goodbye en vez de hello. El motivo es porque la función 'callback' de timeout es precisamente eso - una llamada- por lo que no evalúa el valor de some_var hasta que se pone en marcha. SEFs nos proporciona una solución para este problema. En vez de especificar el 'callback' en timeout, lo devolvemos dentro de una SEF(self-executing function), en la cual pasamos el actual valor de some_var como argumento. Esto significa que aislamos el actual valor de some_var protegiendolo de cualquier cosa que le ocurra a la variable más tarde. Es como tomar una foto antes de pintar un coche, el coche será de otro color pero en la foto será como al principio.
var someVar = 'hello';  
setTimeout((function(someVar) {  
   return function()  { alert(someVar); }
})(someVar), 1000);  
var someVar = 'goodbye';
Ahora el todo funciona como lo habíamos deseado, el alert muestra el valor que tiene some_var en la versión encapsulada en vez del valor que adquiere some_var fuera.

El navegador

8.FIREFOX LEE Y RETORNA LOS COLORES EN RGB EN VEZ DE HEX

Hello, world!  
<script>  
var ie = navigator.appVersion.indexOf('MSIE'!= -1);  
var p = document.getElementById('some_para');  
alert(ie ? p.currentStyle.color : getComputedStyle(p, null).color);</script>
Veamos este ejemplo; la mayoría de navegadores mostraran ff9900 mientras que Firefox devuelve rgb(255,153,0). Os dejo una función de como convertir un color RGB a HEX:
function component_to_hex(c){  
   var  hex = c.toString(16);
   return  hex.length  1 ? '0' + hex : hex; 
}
function rgb_to_hex(r,g,b){  
   return '#'  +  component_to_hex(r) + component_to_hex(g) + component_to_hex(b);
}
alert(rgb_to_hex(0,51,255));  //#0033ff

Miscellaneous

9. 0.1 + 0.2 ! 0.3

Esta rareza no solo afecta a Javascript sino que es un problema en las ciencias de la computación, afecta a varios lenguajes. El resultado es 0.300000000000004. Ahora entra en juego el concepto de precisión de la máquina. Cuando javascript intenta ejecutar la línea de código convierte los valores a sus equivalentes binarios y aquí es donde empieza el problema. Los valores al ser traducidos en binario pierden su valor original pero son casi idénticos al original. Las soluciones para este problema son un tema favorita en los foros de informática y desarrolladores. La elección, se reduce a la clase de cálculos que estás haciendo. Los pros y los contras de cada uno están más allá del alcance de este artículo, pero la elección es entre los dos siguientes casos:
  1. Convertirlos a enteros, hacer el cálculo y después convertirlos a decimales.
  2. Utilizando la lógica para usar un rango de valores en vez de un resultado específico.
Por ejemplo,en vez de hacer esto:
var num_1 = 0.1, num_2 = 0.2, should_equal = 0.3;  
alert(num_1 + num_2  should_equal); //false
Podríamos hacer esto otro:
alert(num_1 + num_2 > should_equal - 0.001 && num_1 + num_2 < should_equal + 0.001); //true
Explicación acerca de los decimales en Javascript

10. UNDEFINIED PUEDE SER DEFINIDO

En realidad undefinied no es una palabra reservada en javascript, a pesar de que tiene un significado especial y es la única manera de determinar si una variable es indefinida o no Entonces.
var some_var;  
alert(some_var === undefined); //evaluates true

Hasta ahora de lo más normal pero:

undefined = "I'm not undefinied";  
var some_var;  
alert(some_var == undefined); //evaluates false

Aquí os dejo las palabras reservadas en javascript

Espero que os sirva de ayuda, a mí me ha aclarado ciertos conceptos y si veis algún error ya sabéis, comentadlo!!

Fuente: SmashingMagazine.com

comments powered by Disqus