Tras comentar con un colega el último post sobre Duck Typing, me lanzó un desafío: crear la función isArray() definitiva para Javascript.
Es cierto que la mayoría de lenguajes poséen un método para identificar si el tipo de datos de una variable corresponde con un Array, pero Javascript no es uno de ellos. Es por eso que hace falta implementarlo por nosotros mismos igual que hacen las principales librerías como jQuery, Mootools o Dojo.
Después de hacer muchas pruebas y refactorización, he llegado a dos soluciones válidas para todos los casos: una utilizando la herencia prototípica y la otra siguendo la metodología del Duck Typing.
Ambas han sido probada en varios entornos con idénticos resultados en rendimiento y fiabilidad.
Método 1 (herencia o prototípica)
function isArray(obj){ return Object.prototype.toString.call( obj ) === "[object Array]"; } |
Este método, parcialmente modificado, es el utilizado por ejemplo por Prototype o Underscore.
Método 2 (mediante el Duck Typing)
function isArray(obj){ return obj && !!obj.push; } |
Probando las funciones
Tomando las siguientes declaraciones:
var a = [], b = [1,2,3,4], c = new Array(1,2,3,4), d = 'Hello World'; |
Ambas funciones devuelven el resultado esperado:
console.log( isArray( a ) ); // true console.log( isArray( b ) ); // true console.log( isArray( c ) ); // true console.log( isArray( d ) ); // false |
A diferencia de instanceof(), tampoco tienen problemas en detectar el tipo a través de iframes:
var iframe = document.createElement( 'iframe' ); document.body.appendChild( iframe ); xArray = window.frames[window.frames.length-1].Array; var arr = new xArray(1,2,3); // [1,2,3] console.log( arr instanceof Array ); // false console.log( arr.constructor === Array ); // false console.log( isArray( arr ) ); // true |
Finalmente, ambas distinguen correctamente entre el objeto arguments (tan similar a un array) y un array genuino:
function test(){ return arguments; } console.log( isArray( test( 1, 2, 3, 4 ) ) ); // false |
En definitiva, cualquiera de estas dos aproximaciones funcionan correctamente y permiten averiguar si el objeto evaluado es o no un array.
Hola, antes que nada quiero felicitarlos por la clara explicación de algo que en principio parece sencillo pero que no lo es tanto. Les escribo para que si fuese posible me expliquen el porqué de la doble negación al método push (metodo 2)… es decir, no es lo mismo preguntar «if( obj.push )» que «if( !!obj.push )» ?
Gracias por adelantado.
Saludos.
Gaston.
Hola Gastón;
la doble negación se utiliza en Javascript como método de coerción de valores.
Cualquier valor del tipo falsy, se convierte así en un booleano puro. Por tanto, es diferente un ‘if(!!var)’ a un simple ‘if(var)’.
Tienes más datos sobre este comportamiento en los siguientes enlaces:
http://www.etnassoft.com/2011/04/06/coercion-de-datos-en-javascript/
http://www.etnassoft.com/2011/02/07/duck-typing-en-javascript-chequeando-los-tipos-de-datos/
Saludos!!
Hola, excelente blog. Una pregunta con el Método 2 (mediante el Duck Typing) es posible que se «confunda» si yo creo un objeto (no sé… myPila) en el cual deba de declararle el método push, que pasaría en este caso?? seguiría funcionando ó hay que tener cuidado con no declarar esté método en las clases ??
Saludos… de antemano gracias por la respuesta
Hola Uziel;
ese es el problema del Duck Typing: que si solo comprobamos un determinado método en un objeto, corremos el riesgo de que en él, se haya definido uno con el mismo nombre que estamos testeando. De ahí que, por lo general, preguntemos siempre por más de uno.
De todos modos, el Duck Typing es, como suelo decir, una comprobación ‘de andar por casa’; no deberíamos depender de él en una aplicación crítica donde la validación de datos es fundamental. Para este tipo de escenarios, podemos utilizar otros métodos más robustos como por ejemplo el descrito en: ‘Cómo obtener el tipo de datos preciso de una variable en Javascript‘.
Saludos!