Com obtenir el tipus de dades precís d’una variable en Javascript

Introducció

Una de les característiques de l’llenguatge Javascript més controvertides és la seva tipat tou o dinàmic de què ja hem parlat en alguna ocasió. Això vol dir que, a diferència d’altres llenguatges clàssics com C o Java, quan vam declarar variables no necessitem especificar el tipus de dades que han de contenir. És més, una mateixa variable, pot posseir diversos tipus diferents en diferents moments d’execució: pot començar sent un sencer, passar a ser un array i més endavant, per exemple, acabar sent un objecte.

Aquesta flexibilitat té avantatges molt interessants per al desenvolupament d’aplicacions complexes ja que no existeixen a priori limitacions pel que fa al que una variable pot emmagatzemar. No obstant això, per a aquells que provenen d’altres llenguatges de tipat dur, un dels problemes d’aquest concepte és que resulta molt difícil identificar el tipus de dades concret que una variable té en un moment donat de l’execució.

identificar el tipus

Efectivament, Javascript posse un mitjà unívoc per a identificar el tipus de dades que conté una variable en un moment donat. De forma nativa, tenim dues instruccions que poden donar-nos un valor aproximat però que, malauradament, no resulten definitives: typeof i instanceof.

L’operador typeof

typeof és un operador unari, el que vol dir que només accepta (i opera) sobre un únic argument; en aquest cas una variable:

a

 typeof 2; // numbertypeof 'Hello World' // stringtypeof // object 

a

Aquest operador no és una funció; però, poden utilitzar-se parèntesi per agrupar els termes a avaluar:

a

 typeof (2); // numbertypeof ( 'Hello World'); // stringtypeof ( 'foo', 4); // number 

a

NOTA: Les agrupacions d’arguments en Javascript determinen el valor final d’un element mitjançant l’ús d’operadors interns. Aquests operadors indiquen l’ordre en què són avaluats els termes, com la coma, la qual realitza aquesta avaluació d’esquerra a dreta per tornar el valor de l’segon operant. Per a més informació sobre aquest aspecte de el llenguatge, es recomana l’article The Javascript Comma Operator.

La següent taula mostra la correspondència d’objectes Javascript i el valor obtingut amb typeof:

Tipus Resultat
undefined “undefined”
Null “object”
boolean “boolean”
Number “number”
string “string”
Host Object ( dins l’entorn JS) depèn de la implementació
Objecte function “function”
Objecte XML E4X “xml”
Objecte XMLList E4X “xml”
Qualsevol altre objecte “object

Com podem observar , No hi ha referència a la taula anterior a elements com els arrays, dates, expressions regulars, etc … Això vol dir que per typeof, aquests elements són un objecte més. A més, veiem alguns resultats que poden semblar confusos com el de null en el qual obtenim com a tipus de nou un ‘object’:

a

 typeof NaN; // numbertypeof Infinity; // numbertypeof (1/0); // numbertypeof (typeof); // string 

a

Sembla que no podem confiar massa en aquest operador per determinar el tipus de dades que avaluem, pel que pot ser interessant buscar una altra solució més segura o completa.

Object.prototype.toString

La funció toString, retorna una cadena que representa l’objecte que se li indica com a argument:

a

 Object.prototype.toString (); // 

a

Observem el resultat: “”: segons l’especificació ECMASCript5, Object.prototype.toString torna com a resultat la concatenació de la cadena ‘object’ més el valor intern de l’objecte que es passa (això que anomenem classe – Class) …

]

Tots els objectes Javascript tenen una propieadad interna coneguda com] (la notació amb doble claudàtor és la mateixa que s’utilitza en l’especificació ES5). Segons el ES5,] és una cadena amb un valor únic (no editable) que identifica cada objecte. D’aquí que un objecte invocat amb el constructor i que no ha estat modificat, retorni com a valor d’aquesta propietat el tipus d’objecte precís a què pertany:

 var o = new Object (); o.toString (); // 

a

No obstant això, veiem que aquest operador també resulta fràgil quan ho apliquem sobre objectes comuns:

 .toString (); // "foo, bar, 1" "Hello World" .toString (); // "Hello World" /a-z/.toString (); // "/ a-z /" 

a

Això és així perquè els objectes personalitzats sobreescriuen majoritàriament el mètode Object.prototype.toString amb els seus propis. Una manera de solucionar això és invocar aquest mètode directament des de l’objecte Object i utilitzar la funció call per injectar l’argument desitjat:

a

 Object.prototype.toString.call (); // Object.prototype.toString.call ( "Hello World"); // Object.prototype.toString.call (/ a-z /); // 

a

D’aquesta manera, evitem sobreescriure res i el resultat obtingut és l’esperat: tenim el tipus de dada correcta.

Creant una funció per determinar el tipus de dades

Extreta directament de l’article d’Angus Croll, podem utilitzar la següent funció per obtenir el tipus correcte de dades d’una variable o objecte:

a

 var toType = function (obj) {return ({}). toString.call (obj) .match (/ \ s (+) /). toLowerCase ()} 

a

Examinémosla per parts:

  • ({}). toString és un abreujament (1 shortcut) de Object.prototype. toString ja que, en tot objecte nou, el mètode toString es refereix a la definción donada per Object.prototype com hem vist més amunt.
  • call l’utilitzem aquí perquè el mètode anterior s’efectuï sobre l’argument que indiquem , en aquest cas, un altre objecte.
  • matx: fem servir una expressió regular per extreure el tipus de dades sense la caden a inicial ‘object’ i els claudàtors. Fem servir una expressió regular en lloc d’un slice o un altre mètode a causa de el millor rendiment que aquesta ofereix sobre la resta.
  • toLowerCase: passem la] a minúscules per diferenciar el tipus del que seria la referència a la instància d’un objecte i que, en general, s’escriu en majúscules.

Vegem com es comporta la funció:

 toType ({a: 4}); // "object" toType (); // "array" (function () {console.log (toType (arguments))}) (); // argumentstoType (new ReferenceError); // "error" toType (new Date); // "date" toType (/ a-z /); // "regexp" toType (Math); // "math" toType (JSON); // "json" toType (new Number (4)); // "number" toType (new String ( "abc")); // "string" toType (new Boolean (true)); // "boolean" 

a

I comparem-la amb el que obtindríem amb typeof:

a

 typeof {a: 4}; // "object" typeof; // "object" (function () {console.log (typeof arguments)}) (); // objecttypeof new ReferenceError; // "object" typeof new Date; // "object" typeof / a-z /; // "object" typeof Math; // "object" typeof JSON; // "object" typeof new Number (4); // "object" typeof new String ( "abc"); // "object" typeof new Boolean (true); // "object" 

a

La diferència en la majoria dels casos és important: de la imprecisió de typeof passem a obtenir tipus concrets.

Comparació amb instanceof

l’operador instanceof revisa la cadena prototípica del primer operant buscant la presència de la propietat prototípica de el segon, el qual s’espera que correspongui amb un constructor:

a

 new Date instanceof Dóna't; // true instanceof Array; // true 

a

El problema d’aquest operador és que alguns objectes en Javascript no tenen associat un constructor per la qual cosa no poden ser avaluats correctament per instanceof:

a

 Math instanceof Math // TypeError 

a

També hi ha el problema de aquells entorns amb múltiples frames on la presència de diversos contextos globals (un per cada frame) impedeixen garantir que un objecte donat sigui una instància d’un determinat constructor:

 var iFrame = document.createElement ( 'iframes'); document.body.appendChild (iFrame); var IFrameArray = window.frames.Array; var array = new IFrameArray (); array instanceof Array; // falsearray instanceof IFrameArray; // true; 

a

Limitacions

La funció toType no pot prevenir-nos d’errors davant de tipus de dades desconegudes:

a

 Object.toType (fff); // ReferenceError 

a

Concretament, és la crida a toType la qual llança l’error, no la funció en si mateixa. L’única manera de prevenir això seria utilitzant la comparació implícita que ens permet el sistema de circuit curt en Javascript:

a

window.fff & & Object.toType ( fff ) ; 

a

Si la primera condició es compleix, es continua amb la següent ; en cas contrari , es talla el circuit i s’evita l’error .

Conclusió

En Javacript , no podem determinar amb precisió el tipus de dades d’una variable utilitzant els mètodes nadius que proporciona el propi llenguatge. En general, quan necessitem identificar tipus , solem recórrer a tècniques de aproximanción com el Duck Typing . No obstant això, en aplicacions on la integritat és crítica, aquesta tècnica no permet aquest rigor exigit.

Tant typeof com instanceof no ofereixen un control rigorós , fiable i unívoc sobre els tipus que podríem necessitar durant un desenvolupament . Per mirar de solucionar aquesta mancança, fem servir algunes de les peculiaritats de l’ llenguatge com el valor ] , per aconseguir un resultat molt més precís.

Aquest article és una traducció expandida de l’original d’Angus Croll : Fixing the JavaScript typeof operator .

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *