3
\$\begingroup\$

I've written this "little" typeof extension for my JS. What do you think about it?

Its aim is to provide a single reliable typeof in JavaScript, consistent between native vars and objects. I've tested it both in FF 4+, IE 7+, Chrome 12, Safari 5. Also, if you have a different browser (also the version), can you test it?

typeOf = function(e) {
  if (typeof e === "undefined") {
    return "undefined";
  } else if (typeof e === "object") {
    if (e === null) {
      // null è un oggetto
      return "null";
    } else if (e instanceof String) {
      return "string";
    } else if (e instanceof Number) {
      return "number";
    } else if (e instanceof Boolean) {
      return "boolean";
    } else if (e instanceof Date) {
      return "date";
    } else if (e instanceof RegExp) {
      return "regexp";
    } else if (e instanceof Error) {
      return "error";
    } else if (e.isArray
                || e instanceof Array) {
      return "array";
    } else if (e instanceof Window) {
      return "window";
    } else if (e.nodeType) {
      switch (e.nodeType) {
        case 1:
          return "element";
        case 2:
          return "attribute";
        case 3:
          return "text";
        case 9:
          return "document";
      }
    } else if (e.call) {
      return "function";
    }
  } else if (typeof e === "function") {
    if (e.call) {
      return "function";
    } else if (e instanceof RegExp) {
      // V8
      return "regexp";
    }
  }

  return typeof e;
};
\$\endgroup\$
1
  • \$\begingroup\$ e.isArray is a bit silly, try Array.isArray(e) \$\endgroup\$
    – Raynos
    Commented Feb 20, 2012 at 16:19

3 Answers 3

7
\$\begingroup\$

I don't think much of it, to be honest. It's not as reliable as you might think, because once you start passing objects between contexts (e.g. different windows) it will fail miserably. Add to that the fact that

Object.prototype.toString.call(<any value>);

could do most of your work for you and your extension starts to look a little redundant and unnecessary.

Here's a detailed article on this problem: Cross-context isArray and Internet Explorer

\$\endgroup\$
3
  • 1
    \$\begingroup\$ damn, you beat me to it by a minute... i'll just add to your answer \$\endgroup\$
    – seand
    Commented Feb 20, 2012 at 16:34
  • 1
    \$\begingroup\$ @seand: lol, thanks for adding a link to my own blog post ;-) I should add that the article there raises a bug in Internet Explorer, but the specific problem mentioned in this answer applies to all browsers. An object created in another window is not an instance of the Object constructor that belongs to the main window. \$\endgroup\$
    – Andy E
    Commented Feb 20, 2012 at 16:37
  • \$\begingroup\$ Wow, I didn't realize! Nice post, it was helpful :) And you're right... you link to another post which focuses more on the general problem: perfectionkills.com/… \$\endgroup\$
    – seand
    Commented Feb 20, 2012 at 17:15
3
\$\begingroup\$

Technically there's nothing wrong with your code, but I think it's a bad approach.

As Andy E mentions, the cool kids are doing it like this now days:

Object.prototype.toString.call(foo);

However, I've personally never found a real use for this either.

Consider this:

if (typeOf(foo) == 'function') { ... }

versus this:

if (foo.call) { ... }

It's actually easier just to do the feature test from your typeOf function than to use the typeOf function itself. It's also clearer what's going on, we don't have to look at typeOf to make sure it's not lying to us, and doing what we expect.

I usually opt for in-line feature testing.

  • function --> foo.call
  • string --> foo.split
  • mutable array --> foo.push
  • immutable array-like thing --> foo.length

Usually I find myself doing this because I wrote a function which accepts, say, either an object or a string, and follows two different code paths depending on what kind of argument it gets. I can fairly safely assume it is one of the accepted types (otherwise it's user error), so my tests don't really need to be that complex. Food for thought, perhaps.

\$\endgroup\$
2
  • \$\begingroup\$ I would avoid this approach (duck typing) because it's not 100% accurate. But this would work in specific cases where you know exactly what your inputs will be (as opposed to a generic reusable function). \$\endgroup\$
    – seand
    Commented Feb 20, 2012 at 18:09
  • \$\begingroup\$ @seand that's the thing, I've personally never run into a situation where I don't know at least roughly what I expect the inputs to be, so duck typing has always worked fine and I've never seen a use for a "general-purpose" function. I'm having trouble imagining a situation where this would be useful. \$\endgroup\$
    – Dagg
    Commented Feb 20, 2012 at 18:27
0
\$\begingroup\$

Outside of the other answers about the general utility of this function, I figured I would add some additional thoughts on the code itself. I do agree with those other opinions though that this function just seems to obfuscate your code behavior. 'typeof' is so universally used and understood, that I don't know why you would want to introduce this potential point of complexity and confusion.

The first thing goat jumps out to me is all the unnecessary nesting. You are doing a good job of returning from the function as quickly as possible, but then still leave a bunch of unnecessary else code paths. If you return in the if. The else should not be there. Remove them and de-nest your code. You will also find this would eliminate need to check for function or regex multiple times.

I do not understand why you would return lowercase first characters in your return values. If your are testing "Object" for example why return "object"? This especially seems odd when your very last line of code for the fall through use case does not apply this same pattern.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.