views:

82

answers:

2

Apparently neither instanceof nor typeof deliver in terms of correctly identifying the type of every javascript object. I have come up with this function and I'm looking for some feedback:

    function getType() {

        var input = arguments[0] ;

        var types = ["String","Array","Object","Function","HTML"] ; //!! of the top of my head

        for(var n=0; n < types.length; n++) {

            if( input.constructor.toString().indexOf( types[n] ) != -1) {
                document.write( types[n] ) ;
            }

        }

    }

Thanks for reading!

+2  A: 

Relying on the instanceof operator is not good enough for some cases.

A known problem is that it wonk doesn't work on cross-frame environments.

The typeof operator is not so useful, and there are some implementation bugs, for example like in Chrome or Firefox 2.x, where RegExp objects are detected as "function", because they have made callable (e.g. /foo/(str);).

The constructor property can be tampered, and you should never put much trust on it.

And finally, the Function.prototype.toString method is implementation dependent, meaning that the implementation may not include even the function name in the string representation of the function...

Some days ago I was building a simple but robust type detection function, it uses typeof for primitive values, and relies on the [[Class]] internal property for objects.

All objects have this property, implementations use it internally to detect the kind of the object, it is completely immutable, and is only accessible through the Object.prototype.toString method:

Usage:

//...
if (typeString(obj) == 'array') {
  //..
}

Implementation:

function typeString(o) {
  if (typeof o != 'object')
    return typeof o;

  if (o === null)
      return "null";
  //object, array, function, date, regexp, string, number, boolean, error
  var internalClass = Object.prototype.toString.call(o)
                                               .match(/\[object\s(\w+)\]/)[1];
  return internalClass.toLowerCase();
}

The second variant of this function is more strict, because it returns only built-in object types described in the ECMAScript specification.

Possible output values:

Primitives:

  • "number"
  • "string"
  • "boolean"
  • "undefined"
  • "null"
  • "object"

Built-in object types (through [[Class]])

  • "function"
  • "array"
  • "date"
  • "regexp"
  • "error"
CMS
You've earned your 74K reputation, CMS.
Diodeus
Good stuff. Thanks! Can you elaborate on why using the `constructor` property is not a good idea as it can be "tampered"?
FK82
@Diobeus, thanks!, @FK82 I mean that the `constructor` property is writable, and a malformed object can give you wrong results, e.g. (`var re = /foo/; re.constructor = null;`), also you might set the wrong `constructor` when implementing inheritance on custom types [without even noticing](http://joost.zeekat.nl/constructors-considered-mildly-confusing.html)...
CMS
@ CMS Alright, thanks again. So, inheritance screws up the constructor. There is no java in javascript after all, is there? :-)
FK82
@FK82: Overriding the entire `prototype` member of constructor functions can screw the `constructor` property... Yes JavaScript is more like "Lisp in C's clothing" :-)
CMS
Could the downvoter leave a comment? Otherwise the downvote is pointless...
CMS
+1  A: 

Similar question came up a few days ago. I cracked open jQuery 1.4.2 to see how it has been done there. Here are my results thus far, you can run checks for the rest of them Im sure:

(function() {

    // Define the base sys namespace
    this.sys = function() { };

    var toString = Object.prototype.toString;

    //from jQuery 1.4.2    
    sys.isFunction = function(obj) {
        return toString.call(obj) === "[object Function]";
    }

    //from jQuery 1.4.2
    sys.isArray = function(obj) {
        return toString.call(obj) === "[object Array]";
    }
}

Usage:

if (sys.isArray(myObject)) doStuff();
James Westgate
Looks similar to CMS's technique.
James Westgate
Yeah. They both seem to be using the same mechanism. Thanks anyway.
FK82