4

I'm wondering if it's appropriate to use Object.prototype.toString() for general type checking with built-in types. I have a function that looks like this:

// Return the built-in type of an object.
var typeOf = (function() {
  var reType = /\[object (\w+)\]/; 
  return function typeOf(obj) {
    return reType.exec(Object.prototype.toString.call(obj))[1];
  };
})();

Calls to the function return the following results:

console.log( typeOf(null) );         // => Null
console.log( typeOf(undefined) );    // => Undefined
console.log( typeOf([]) );           // => Array
console.log( typeOf(true) );         // => Boolean
console.log( typeOf(new Date()) );   // => Date
console.log( typeOf(new Error()) );  // => Error
console.log( typeOf(function(){}) ); // => Function 
console.log( typeOf(1) );            // => Number
console.log( typeOf({}) );           // => Object
console.log( typeOf(/ /) );          // => RegExp
console.log( typeOf("") );           // => String

Is this acceptable other than the fact it might be slower than other forms of type checking?

One of the reasons I'm asking is because I'm wanting to encode and serialize an object's built-in type for a project I'm working on. I'm looking at passing the returned type to a function which returns a numeric code:

// Encode a built-in type as a number.
var encodeType = (function() {
  var types = {
    'Null':      0,
    'Undefined': 1,
    'Array':     2,
    'Boolean':   3,
    'Date':      4,
    'Error':     5,
    'Function':  6,
    'Number':    7,
    'Object':    8,
    'RegExp':    9,
    'String':    10,
    'Arguments': 11,
    'Math':      12,
    'JSON':      13
  };
  return function encodeType(type) {
    return types[type];
  }
})();

So the output becomes:

console.log(encodeType( typeOf(null) ));         // => 0
console.log(encodeType( typeOf(undefined) ));    // => 1
console.log(encodeType( typeOf([]) ));           // => 2
console.log(encodeType( typeOf(true) ));         // => 3
console.log(encodeType( typeOf(new Date()) ));   // => 4
console.log(encodeType( typeOf(new Error()) ));  // => 5
console.log(encodeType( typeOf(function(){}) )); // => 6
console.log(encodeType( typeOf(1) ));            // => 7
console.log(encodeType( typeOf({}) ));           // => 8
console.log(encodeType( typeOf(/ /) ));          // => 9
console.log(encodeType( typeOf("") ));           // => 10

Are there any pitfalls with type checking this way? Thanks for any insights.

t.888
  • 3,862
  • 3
  • 25
  • 31
  • I imagine somewhere inside javaScript there is an enum for all the types. Probably beyond your access. – Yetti99 Oct 27 '14 at 19:02
  • 1
    Looks good. Another alternative if you don't actually need to use the type info in the program is to use a type-enforcing compiler, eg the closure compiler supports type annotations. – Jared Smith Oct 27 '14 at 19:02
  • 1
    This all looks good. I started fiddling with `eval("obj instanceof " + types[i])` to see if that gives you a safer way to do it, but it just returns `Object` for some like Numbers and Booleans - http://jsfiddle.net/wfL9bfs9/ – Rhumborl Oct 27 '14 at 19:12
  • 1
    In fact this page does the same as you are suggesting: http://javascript.info/tutorial/type-detection#class-to-differ-between-native-objects – Rhumborl Oct 27 '14 at 19:16
  • 1
    Is there a question lurking in here somewhere? Yes, people use `Object.prototype.toString` all the time for type checking. –  Oct 27 '14 at 19:34
  • Questions are in the form `/^[\w ]+\?/`. – t.888 Oct 27 '14 at 19:47
  • 1
    Yes, it is acceptable, no, it not particularly slower than other types of type checking, and no, there are no pitfalls I can think of. –  Oct 27 '14 at 21:04
  • Duplicate of [How do I get the name of an object's type in JavaScript?](http://stackoverflow.com/q/332422/1048572)? – Bergi Oct 27 '14 at 21:18
  • 1
    IMO, your implementation is enought good, also you can save a local cache to the chain reference in the IIFE closure: **`var toString = Object.prototype.toString`** or creating the alias by passing the reference as argument in the IIFE. – jherax Oct 27 '14 at 21:29
  • Thanks for the helpful comments, all. Sounds like this is a solid way to get what I'm after. I was expecting some caveats, but so far it looks good. – t.888 Oct 27 '14 at 23:24

2 Answers2

3

Here is underscore.js' implementation:

  // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
  _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
    _['is' + name] = function(obj) {
      return toString.call(obj) === '[object ' + name + ']';
    };
  });

So yes, this method is good to go.

P.S. toString is short for Object.prototype.toString in the above code.

Lodewijk Bogaards
  • 19,777
  • 3
  • 28
  • 52
0

I don't understand all of the extra syntax. Wouldn't this do the exact same thing?

var typeOf = function(obj) {
    var reType = /\[object (\w+)\]/;
    return reType.exec(Object.prototype.toString.call(obj))[1];
    };

Also, it might be better to use a switch for encodeType

Jonathan Gray
  • 2,509
  • 15
  • 20
  • The only difference is that the closure suggests that the regular expression be compiled only once instead of every time the function is called (see the second answer here: http://stackoverflow.com/questions/2249008/javascript-regex-compile). It may or may not make a concrete difference, I haven't timed it. – t.888 Oct 27 '14 at 20:59
  • I'm referring to the extra return function and the extra enclosure around your typeOf function which I have removed in my sample above. – Jonathan Gray Oct 27 '14 at 21:03
  • I tend to avoid switch statements in Javascript where possible. It's personal preference - I think the object literal notation is cleaner and easier to read. I don't know whether it's faster than a switch statement or not. – t.888 Oct 27 '14 at 21:05
  • Cleaner and easier to read yes, but you're not accounting for unexpected input and the variable is being recreated each time the function is called. – Jonathan Gray Oct 27 '14 at 21:06
  • That's what I was getting at. The closure is an immediately invoked function expression and is executed exactly once, so the regular expression is compiled once and available to the returned function for when it's called. It's a minor optimization. Your simplified version should behave the same way, except that the regular expression will be compiled every time the `var reType=...` expression is evaluated when the function is called. With the closure, that expression is evaluated only once. – t.888 Oct 27 '14 at 21:11
  • Any input that is not defined on the object literal will return `undefined`. Since the input comes directly from `Object.prototype.toString`, there is a predefined set of returned values; `[object Array]`, `[object String]`, etc., that can be present. I could also add a check for 'undefined' before returning the encoded type, which I may in fact do. ;) I may also move the object declaration outside the function, but this was a clean way to present it for consumption. Thanks much for the suggestions. – t.888 Oct 27 '14 at 21:19
  • 1
    Okay, i see what you mean. The link you referenced made me think you were referring to something completely different. I understand you're basically making a private persistent variable to hold the regex for your function. Just seemed like it could be more complicated for the interpreter. I guess it depends on the interpreter's built-in optimizations. Thanks for the explanations. I've used something similar to protect encryption keys from bein snooped from the JavaScript console for a password manager extension I made for Chrome. – Jonathan Gray Oct 27 '14 at 21:26