Coercion en JavaScript

¿Que es coerción?

Se podría decir que la 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, esto hace que en tiempo de ejecución podamos cambiar el valor de las variables sin ningún problema.

JavaScript soporta varias formas de comprobar el tipo de un objeto (duck typing) y una forma de saberlo es a través del operador typeof.

Una de las ventajas de esta característica, es que podemos comprobar la existencia de un objeto mediante su valor, JavaScript se encarga de hacer coerción al tipo boolean en una expresión lógica, por ejemplo:

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

    if (var1) { } // -> coerción a false
    if (var2) { } // -> coerción a true

    if (!var1) { } // -> coerción undefined a true
    if (!var2) { } // -> coerción 9 a false

    if (!!var1) { } // -> boolean: false
    if (!!var2) { } // -> boolean: true

    //asigna 0 si var1 hace coerción a false
    var1 = var1 || 0;

    //boolean absoluto: false
    var value = !!("" || null);

Nótese el uso del operador !(NOT) el cual toma el operando de la derecha y lo fuerza a su representación boolean pero negado. Es decir que si la coerción sobre un objeto lo resuelve como true, entonces el operador !(NOT) lo invierte a false.

De igual forma opera la doble negación !!(DOUBLE NOT), de manera que si el primer !(NOT) invierte el valor boolean, entonces el segundo !(NOT) nuevamente lo invierte para obtener el valor boolean absoluto de un objeto.

Valores Truthy and Falsy

Conocer los valores falsos que resultan de una comparación o de la coerción forzada que hace JavaScript es muy importante y puede ayudarnos a evitar errores de lógica en nuestro código. La siguiente tabla de valores falsy está basada en ECMAScript 5.

falsy value   boolean coercion
false false
0 false
“” false
NaN false
null false
undefined false

Todos los demás valores son verdaderos/truthy, incluyendo los textos "0" y "false" (entre comillas), las funciones, arrays y objetos vacíos.

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

    (function (u) { //u = undefined
     
        var fn = function() {},
            o = { valueOf: function() { return 7 } };
     
        //coerción en diferentes tipos de objetos
        write("a.", fn);   //!!bool: true,  type: function
        write("b.", fn()); //!!bool: false, type: undefined
        write("c.", u);    //!!bool: false, type: undefined
        write("d.", 0);    //!!bool: false, type: number
        write("e.", "");   //!!bool: false, type: string
        write("f.", +"");  //!!bool: false, type: number
        write("g.", +"x"); //!!bool: false, type: number (NaN)
        write("h.", null); //!!bool: false, type: object [Null]
        write("i.", []);   //!!bool: true,  type: object [Array]
        write("j.", {});   //!!bool: true,  type: object [Object]
        write("k.", o);    //!!bool: true,  type: object [Object]
        write("l.", +o);   //!!bool: true,  type: number
     
        function padRight (val, len) {
            var c = " ";
            len = len || 10;
            return (val + new Array(len).join(c)).slice(0, len);
        }
     
        function write (nom, val) {
            console.log(nom, "type:", padRight(typeof val),
                "!!bool:", padRight(!!val, 7), "value:", val);
        }
    }());

Nota: En el ejemplo anterior observamos que en los puntos h, i, j, k el operador typeof retorna "object" como tipo de dato, y esto puede conllevar a errores ya que no se diferencia correctamente un Array de un Object o de otras clases/constructores. Para solucionar este inconveniente, podemos usar el método toString del prototipo de Object para detectar el tipo/clase del objeto.

    var toString = Object.prototype.toString;

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

Nota: De lo anterior podemos concluir que la forma correcta de usar el operador typeof es con los objetos de tipo [string,number,boolean,function,undefined] y para determinar el tipo de dato en los demás objetos, se recomienda usar Object.prototype.toString

Otros ejemplos de coerción

    (function () {
        //accediendo elementos del array
        var array = ["cero", "uno", "dos"];
        console.log("a.", array[+[]]);   //coerción a number
        console.log("b.", array[+!![]]); //coerción a number
        console.log("c.", array['1']);   //coerción a number
        console.log("d.", array[ 2 ]);
    
        //accediendo propiedades de un objeto
        var obj = { id: 1, "5": "cinco" };
        console.log("e.", obj['5']);  //propiedad de obj
        console.log("f.", obj[ 5 ]);  //coerción a string
    
        //coerción con números y strings
        console.log("g.", 5 - '3');      // -> 2
        console.log("h.", 5 + '3');      // -> "53"
        console.log("i.", 5 + (+'3'));   // -> 8
        console.log("j.", 5 * '3');      // -> 15
        console.log("k.", "" - 3);       // -> -3
        console.log("l.", true + 1);     // -> 2
        console.log("m.", '1' + true);   // -> 1true
        console.log("n.", '1' - true);   // -> 0
        console.log("o.", false - true); // -> -1
    
        //coerción con fechas
        var d = new Date();
        console.log("p.", +d);     //number, llama a .valueOf()
        console.log("q.", d + ""); //string, llama a .toString()
    }());

Nota: El operador unario + es de mucha utilidad a la hora de convertir string a number, opera de una forma muy similar a las funciones parseFloat() y parseInt(), con una diferencia, y es que mientras los métodos mencionados convierten los primeros dígitos numéricos de una cadena a su representación numérica, el operador unario +, evalúa toda la cadena y la intenta convertir a número, veamos el siguiente ejemplo:

    console.log( +"13px" ); //NaN
    console.log( parseFloat("13px") ); //13

Conclusión

La coerción es una de esas características de JavaScript que es bueno conocer, de manera que podamos evitar errores comunes y errores de lógica (más difíciles de rastrear), y sin duda mejora nuestras habilidades para programar, explotando las características del lenguaje.

Recomiendo que revisen los artículos JavaScript Desmitificado: Coercion y también Coerción de datos en Javascript.

Happy coding!

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s