tags:

views:

823

answers:

4

Just curious:

  • 4 instanceof Number => false
  • new Number(4) instanceof Number => true?

Why is this? Same with strings:

  • 'some string' instanceof String returns false
  • new String('some string') instanceof String => true
  • String('some string') instanceof String also returns false
  • ('some string').toString instanceof String also returns false

For object, array or function types the instanceof operator works as expected. I just don't know how to understand this.

[re-opened to share new insights]

After testing I cooked up this functionality to be able to retrieve the type of any javascript object (be it primitive or not):

Object.prototype.typof = objType;    
objType.toString = function(){return 'use [obj].typof()';};
function objType() {
      var inp = String(this.constructor),
          chkType = arguments[0] || null,
          val;
      function getT() {
         return (inp.split(/\({1}/))[0].replace(/^\n/,'').substr(9);
      }
      return chkType
           ? getT().toLowerCase() === chkType.toLowerCase()
           : getT();
}

Now you can check any type like this:

var Newclass = function(){}; //empty Constructor function
(5).typof(); //=> Number
'hello world'.typof(); //=> String
new Newclass().typof(); //=> Newclass

Or test if an object is of some type:

(5).typof('String'); //=> false
new Newclass().typof('Newclass') //=> true
[].typof('Arrray'); //=> true;
+15  A: 

value instanceof Constructor is the same as Constructor.prototype.isPrototypeOf(value) and both check the [[Prototype]]-chain of value for occurences of a specific object.

Strings and numbers are primitive values, not objects and therefore don't have a [[Prototype]], so it'll only work if you wrap them in regular objects (called 'boxing' in Java).

Also, as you noticed, String(value) and new String(value) do different things: If you call the constructor functions of the built-in types without using the new operator, they try to convert ('cast') the argument to the specific type. If you use the new operator, they create a wrapper object.

new String(value) is roughly equivalent to Object(String(value)), which behaves the same way as new Object(String(value)).


Some more on types in JavaScript: ECMA-262 defines the following primitive types: Undefined, Null, Boolean, Number, and String. Additionally, there is the type Object for things which have properties.

For example, functions are of type Object (they just have a special property called [[Call]]), and null is a primitive value of type Null. This means that the result of the typeof operator doesn't really return the type of a value...

Aditionally, JavaScript objects have another property called [[Class]]. You can get it via Object.prototype.toString.call(value) (this will return '[object Classname]'). Arrays and functions are of the type Object, but their classes are Array and Function.

The test for an object's class given above works when instanceof fails (e.g. when objects are passed between window/frame boundaries and don't share the same prototypes).


Also, you might want to check out my improved version of typeof. For primitives, it will return their type in lower case, for objects, it will return their class beginning in upper case.

Examples:

  • For primitives of type Number (eg 5), it will return 'number', for wrapper objects of class Number (eg new Number(5)), it will return 'Number';

  • For functions, it will return 'Function' (this behaviour changed in the last revision, but is more consistent because functions are objects).

If you don't want to discern between primitive values and wrapper objects, use typeOf(...).toLowerCase().

Known bugs are some built-in functions in IE, which are considered 'Object' and a return value of 'unknown' when used with some COM+ objects.

Christoph
@annakata: check ECMA-262 . the types are called Undefined, Null, Boolean, Number, String and Object; doesn't matter what `typeof` returns...
Christoph
@Christoph - it very much matters since the wrapper objects are uppercase, and the typeofs are lowercase and nothing else is accessible. The convention and practice is therefore to refer to the datatypes in the lowercase as made clear here: https://developer.mozilla.org/En/JS/Glossary
annakata
@annakata: `typeof` is broken - its return value has nothing to do with the actual types!
Christoph
Well I guess you're going with the spec and I'm going with the practice, which in this case I think is in the right. I think it's ill-advised to reference the types and the wrapper objects with the same format, but I'll not edit your post further.
annakata
A: 

You may try to evaluate:

>>> typeof("a")
"string"
>>> typeof(new String("a"))
"object"
>>> typeof(4)
"number"
>>> typeof(new Number(4))
"object"
rkj
A: 

This is a nuance of Javascript which I've found catches some out. instanceof of operator will always result in false if the LHS is not an object type.

Note that new String('Hello World') does not result in a string type but is an object. The new operator always results in an object. I see this sort of thing:-

function fnX(value)
{
     if (typeof value == 'string')
     {
          \\Do stuff
     }
}
fnX(new String('Hello World'));

The expectation is that "Do Stuff" will happen but it doesn't because the typeof the value is object.

AnthonyWJones
+1  A: 

As stated in Christoph's answer, string and number literals are not the same as String and Number objects. If you use any of the String or Number methods on the literal, say

'a string literal'.length

The literal is temporarily converted to an object, the method is invoked and the object is discarded.
Literals have some distinct advantages over objects.

//false, two different objects with the same value
alert( new String('string') == new String('string') ); 

//true, identical literals
alert( 'string' == 'string' );

Always use literals to avoid unexpected behaviour!
You can use Number() and String() to typecast if you need to:

//true
alert( Number('5') === 5 )

//false
alert( '5' === 5 )
meouw