2

Any developer writing JS sooner or later encounters this, rather infuriating behaviour:

typeof []; // 'object'

While there are workarounds such as instanceof and accessing a variable's .constructor property, this just came to my mind:

Array.prototype.TYPE = 'Array';
String.prototype.TYPE = 'String';
Boolean.prototype.TYPE = 'Boolean';
Object.prototype.TYPE = 'Object';
RegExp.prototype.TYPE = 'RegExp';
Number.prototype.TYPE = 'Number';

// and therefore:
[1,2,'bar', {baz: 'foo'}].TYPE === 'Array'; // true
"But life itself, my wife, and all the world / Are not with me esteemed above thy life".TYPE // 'String'
(42).TYPE === 'Number'; // true
// and so on

I know it is generally frowned upon to modify native prototypes, That said, is there any other issue with this "pattern"?

UPDATE: Some comments offer alternative solutions like usage of Object.prototype.toString and more. These are certainly all valid approaches that have their use cases. But still, what I'm primarily interestest in, is whether there might be some case in which adding a property to native constructor prototypes would actually cause a problem :)

UPDATE:

A safer way?

Array.prototype.getType = function() {return 'Array'};
String.prototype.getType = function() {return 'String'};
Boolean.prototype.getType = function() {return 'Boolean'};
Object.prototype.getType = function() {return 'Object'};
RegExp.prototype.getType = function() {return 'RegExp'};
Number.prototype.getType = function() {return 'Number'};
Anatol
  • 191
  • 6
  • I always liked comparing it to an empty/uninitialized version of what you expect... `typeof([1,2]) == typeof([]);` or `typeof({'this':'that'}) == typeof({});` so no predefining or modifying of native protos – xxcezz Jun 06 '14 at 18:58
  • If an object contains the property "TYPE", you won't be able to read yours, because it will shadow yours. What's wrong with checking `Object.prototype.toString.call(obj)`'s result? – Ian Jun 06 '14 at 18:59
  • 3
    @xcezzz this is a nice approach, but what about `typeof {} === typeof [] // true`? – Anatol Jun 06 '14 at 19:00
  • @Ian its useful, no doubt, but for instance native objects will differ in their `toString` value: `Object.prototype.toString.call(JSON) // "[object JSON]"`. I'd expect `"[Object object]"` for all objects. – Anatol Jun 06 '14 at 19:02
  • 2
    @Anatol You just stumbled on why `typeof {} === typeof []` -- "*I'd expect `Object` for all objects.*" They're equal because `Array`s are `Object`s, as are `Function`s, etc. Only primitives values aren't `Object`s. The other identified "*types*" are only given because of special behavior -- e.g. `'function'`s are callable. – Jonathan Lonowski Jun 06 '14 at 19:05
  • 1
    @Anatol But it's not a simple object. An object is created with `{}` or `new Object()` – Ian Jun 06 '14 at 19:05
  • @JonathanLonowski Actually every type in javascript is an Object. String is also an Object. – Derek 朕會功夫 Jun 06 '14 at 19:07
  • 1
    @Derek朕會功夫 Yeah, `String`s are `Object`s, but [string values](http://ecma-international.org/ecma-262/5.1/#sec-4.3.16) aren't `String`s until they're boxed (usually by property accessors). [`'foo' instanceof String`](http://jsconsole.com/?%27foo%27%20instanceof%20String) is `false`. – Jonathan Lonowski Jun 06 '14 at 19:09
  • All true, but the behaviour is not consistent. If one defines constructors, objects instanciated from these constructors will be `"[Object object]"`, too. – Anatol Jun 06 '14 at 19:12
  • `function Person(name) {this.name = name;}; Object.prototype.toString.call(Person); //"[object Function]" var paul = new Person('paul'); Object.prototype.toString.call(paul); //"[object Object]"` – Anatol Jun 06 '14 at 19:12
  • Just don't mess with types in javascript because it's so confusing to work with. Normally what I would do is to check primitives with `typeof`, and arrays with `isArray` or `instanceof` and that's it. – Derek 朕會功夫 Jun 06 '14 at 19:19
  • 4
    @Anatol I'd suggesting using "*static*" methods on the constructors themselves, following the example of [`Array.isArray()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray). These won't interfere or clash with instance properties. Underscorejs includes [a collection of such methods](http://underscorejs.org/#objects) that you can use or take as inspiration. – Jonathan Lonowski Jun 06 '14 at 19:22
  • FEI: There are six data types in JS. Five are primitive types: boolean, string, number, undefined, null, one is the object type. Some primitive types have object equivalents (string, number, boolean), some don't (null, undefined). JS defines a couple of built-in objects with specific behavior: Array, Function, RegEx, Date, etc. The problem is that the `typeof` operator somehow does two things: determine the data type of a value (one of the six possibilities) and/or the "kind" of object (e.g. "function"). – Felix Kling Jun 06 '14 at 19:40

1 Answers1

3

Douglas Crockford recommends writing your own typeOf function for exactly this reason:

... The new typeOf global function is intended to replace the defective typeof operator. It produces the same result as typeof, except that it returns 'null' for null and 'array' for arrays.

He implements it thusly:

function typeOf(value) {
    var s = typeof value;
    if (s === 'object') {
        if (value) {
            if (typeof value.length === 'number' &&
                    !(value.propertyIsEnumerable('length')) &&
                    typeof value.splice === 'function') {              
                s = 'array';
            }
        } else {
            s = 'null';
        }
    }
    return s;
}

typeOf({}) //=> "object"

typeOf([]) //=> "array"
user229044
  • 232,980
  • 40
  • 330
  • 338
  • I'll gladly take this advice and add this snippet to my standard build, thanks alot! Still, im interested in exactly why it shouldn't be done as in my original question. – Anatol Jun 06 '14 at 19:45
  • 2
    Because you're changing the expectations of every piece of code that your code will interact with, and every developer who is going to work on your code after you. If there were a really good reason to do so, I'd say go ahead, but this *isn't* a really good reason. You're making a problem where there really isn't one. You want to be able to tell the difference between `[]` and `{}`, but you're modifying six built-in objects to do so, and inventing your own pseudo type system. Go back to your *actual* problem, `[]` vs `{}`, and choose the simpler solution with the smallest footprint. – user229044 Jun 06 '14 at 19:47
  • @meagar your reasoning is flawless, you identified the underlying fallacy with my idea, thanks! – Anatol Jun 06 '14 at 20:04