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 conceptoNaN
(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 dereplace()
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 enmatch()
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 objetowindow
, 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:- Convertirlos a enteros, hacer el cálculo y después convertirlos a decimales.
- Utilizando la lógica para usar un rango de valores en vez de un resultado específico.
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 realidadundefinied
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