Coercion en JavaScript

¿Que es coerción?

Se podría decir que coerción es la acción de forzar a que un objeto se comporte como si fuera de otro tipo. Sin lugar a dudas, éste es uno de los aspectos más interesantes de JavaScript. Al ser un lenguaje débilmente tipado y dinámico, no es necesario especificar el tipo de dato para las variables porque el tipo está asociado al valor (duck typing), esto hace que en tiempo de ejecución podamos cambiar el valor de las variables sin ningún problema.

💡 Una de las ventajas de esta característica, es que podemos comprobar la existencia de un objeto mediante su valor, por lo que JavaScript permite implementar muy fácil algunos principios OOP como la sobrecarga de métodos (overload), sobrescritura de métodos (override) y polimorfismo. Un ejemplo de ello es como jQuery implementa el patrón composite en su constructor, definiendo un método que se puede adaptar dependiendo de la cantidad de argumentos, o del tipo de los argumentos.

Por ahora veamos como funciona la coerción hacia boleanos, es decir, como se comportan otros tipos de datos si se tratan como boleanos.

    var var1, // -> undefined
        var2 = 9;

    if (v1) { } // -> undefined se evalúa como false
    if (v2) { } // -> 9 se evalúa como true

    if (!v1) { } // -> al negar undefined, se evalúa como true
    if (!v2) { } // -> al negar 9, se evalúa como false

    var v3 = !!v1; // -> convierte explícitamente undefined a false
    var v4 = !!v2; // -> convierte explícitamente 9 a true

    // asigna 0 si la coerción v1 es false
    v1 = v1 || 0;

    // ejecuta el alert si la coerción de v2 es true
    v2 && alert("Hey!");

💡 Nótese el uso del operador !(NOT), el cual toma el operando de la derecha y lo evalúa como boolean pero negado. 😎 Este truco se usa bastante para comprobar la existencia de un valor no falsy en una variable.

Así mismo, la doble negación !!(DOUBLE NOT), convierte un valor de cualquier tipo a boolean. 😎 Este truco se usa para comprobar explícitamente si un valor representa true o false.

Truthy and Falsy

Conocer los valores falsy que resultan de una comparación o de la coerción que hace JavaScript es muy importante y nos ayudará a evitar errores de lógica. La siguiente tabla muestra los valores falsy.

falsy coercion
false false
0 false
“” false
NaN false
null false
undefined false
Coercion

➡ Todos los demás valores son evaluados como truthy (boolean: true), incluyendo las funciones, arrays vacíos, objetos vacíos y los string "0" y "false".

Coerción a números

⭐ El operador unario +(plus) se puede utilizar para convertir un string a number, si dicho string representa un número en su totalidad, de lo contrario devuelve NaN (Not A Number).

    n = +"13";  //  13, type: number
    n = +"0";   //   0, type: number
    n = +"5px"; // NaN, type: number

💡 Las funciones parseFloat() y parseInt(), operan de una forma muy similar, pero con una diferencia respecto al operador unario +, y es que los métodos antes mencionados convierten los primeros dígitos numéricos de un string a su representación numérica, aunque ese string no esté compuesto en su totalidad por números:

    n = +"12px"; // NaN, type: number
    n = parseFloat("12px"); // 12, type: number

Coerción en objetos

⭐ Cuando JavaScript intenta hacer coerción en un objeto, primero busca si el método valueOf() retorna un valor primitivo, si no puede hacerlo, entonces intenta obtenerlo del método toString().

Ahora veamos algunos ejemplos de coerción con diferentes tipos:

    var u, // undefined
        fn = function() {}, // retorna undefined
        obj = { valueOf: function() { return 7 } };

    // coerción en diferentes tipos de objetos
    console.log("a.", !!fn);   // !!bool: true,  type: function
    console.log("b.", !!fn()); // !!bool: false, type: undefined
    console.log("c.", !!u);    // !!bool: false, type: undefined
    console.log("d.", !!0);    // !!bool: false, type: number
    console.log("e.", !!"");   // !!bool: false, type: string
    console.log("f.", !!+"");  // !!bool: false, type: number
    console.log("g.", !!+"x"); // !!bool: false, type: number (NaN)
    console.log("h.", !!null); // !!bool: false, type: object
    console.log("i.", !![]);   // !!bool: true,  type: object
    console.log("j.", !!{});   // !!bool: true,  type: object
    console.log("k.", !!obj);  // !!bool: true,  type: object
    console.log("l.", !!+obj); // !!bool: true,  type: number

Para obtener el tipo de un objeto, normalmente utilizamos el operador typeof, pero en el ejemplo anterior observamos que para los items h, i, j, el operador typeof retorna "object" como tipo de dato, 😡 y esto no es conveniente en algunos casos donde necesitamos diferenciar correctamente un Array de un Object o de otros constructores.

💡 Para solucionar este inconveniente, podemos usar el método toString del prototipo Object para detectar el tipo/constructor de un objeto.

    // usando typeof
    typeof ({}) === "object"
    typeof ([]) === "object"
    typeof null === "object"
    typeof Math === "object"

    // usando toString
    var toString = Object.prototype.toString;

    toString.call({});   // [object Object]
    toString.call([]);   // [object Array]
    toString.call(null); // [object Null]
    toString.call(Math); // [object Math]

➡ De lo anterior podemos decir que el operador typeof funciona bien con los tipos primitivos (string, number, boolean, function, undefined) pero para diferenciar mejor los demás tipos (object, null), se recomienda usar Object.prototype.toString()

Más coerción

Finalmente veamos otros escenarios donde se da la coerción:

    // accediendo elementos de un array
    var array = ["cero", "uno", "dos"];
    console.log("a.", array[+[]]);   // coerción a number 0
    console.log("b.", array[+!![]]); // coerción a number 1
    console.log("c.", array['1']);   // coerción a number 1
    console.log("d.", array[ 2 ]);

    // accediendo propiedades de un objeto
    var obj = { id: 1, "5": "cinco" };
    console.log("e.", obj['5']);
    console.log("f.", obj[ 5 ]);  // coerción a string

    // coerción con number, string, boolean
    console.log("g.", 5 - '3');      //= 2, coerción a number
    console.log("h.", 5 + '3');      //= "53", coerción a string
    console.log("i.", 5 + (+'3'));   //= 8, coerción a number
    console.log("j.", 5 * '3');      //= 15, coerción a number
    console.log("k.", "" - 3);       //= -3, coerción a number ("" -> 0)
    console.log("l.", true + 1);     //= 2, coerción a number (true -> 1)
    console.log("m.", '1' - true);   //= 0, coerción a number (true -> 1)
    console.log("n.", '1' + true);   //= "1true", coerción a string
    console.log("o.", false - true); //= -1, coerción a number

    // coerción con fechas
    var d = new Date();
    console.log("p.", +d);     // number, llama a .valueOf()
    console.log("q.", "" + d); // string, llama a .toString()

Coercion

Conclusión

La coerción es una de esas características de JavaScript que es bueno conocer, de modo que entendamos correctamente los fragmentos de código y evitemos cometer errores de lógica, que son más difíciles de rastrear.

➡ Recomiendo que lean el artículo Coerción de datos en Javascript.

😎 Happy coding!

Advertisements

2 thoughts on “Coercion en JavaScript

Comentarios

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s