It's seldom to check the object type. JavaScript is a dynamically typed language so what you have to know are the object features, not the object type. It's enough to do feature detection in most situations. For example:
if(obj.someProperty) {If a property doesn't exist, undefined is returned and treated as false. If it exists, the object is returned and treated as true. These are the basic rules of feature detection.
// do something...
}
If you really want to confirm the object type, there're many ways. But basically, the provided information is either insufficient or suspected.
Take typeof for example, the return value is a string. It returns 'number' for a primitive number, 'string' for a string value, 'boolean' for a boolean value, 'function' for a Function instance, 'undefined' for undefined, and 'object' for all other objects, including null (Supervise XD). Basically, you cannot determine what type an object is if it's not a Function instance.
You can know the constructor function of an object from the constructor property. It's mentioned in Prototype Chains that the prototype object of any function has a constructor property which refers to the function itself. This is a way to check the object type. However, the constructor property is updatable. Maybe no one will update the constructor property, but in the situation of prototype chains:
js> function Person() {}
js> function Car() {}
js> Car.prototype.wheels = 4;
4
js> function SportsCar() {}
js> SportsCar.prototype = new Car();
[object Object]
js> SportsCar.prototype.doors = 2;
2
js> var sportsCar = new SportsCar();
js> sportsCar.doors;
2
js> sportsCar.wheels;
4
js> sportsCar.constructor;
function Car() {
}
js>
js> function Car() {}
js> Car.prototype.wheels = 4;
4
js> function SportsCar() {}
js> SportsCar.prototype = new Car();
[object Object]
js> SportsCar.prototype.doors = 2;
2
js> var sportsCar = new SportsCar();
js> sportsCar.doors;
2
js> sportsCar.wheels;
4
js> sportsCar.constructor;
function Car() {
}
js>
The above example uses prototype chains to implement a so-called inheritance. If JavaScript doesn't find the wheels property on the sportsCar itself, it will search the prototype object of sportsCar. That is SportsCar.prototype. SportsCar.prototype refers to the Car instance but it also has no the wheels property. JavaScript will search the prototype object of the Car instance. That is the object referred by Car.prototype and it's found there.
In the above example, however, if you want to search the constructor property of the SportsCar instance, JavaScript will find Car.prototype.constructor according the same mechanism. The code should fill a line as follows:
SportsCar.prototype.constructor = SportsCar;If you miss this line, the constructor property of the SportsCar instance is not correct.
I've talked about the new operator in Prototype Chains. When you use the new operator, JavaScript creates a plain object, sets the prototype property of the constructor function as the prototype object of it, calls the constructor function and then sets this to refer the plain object.
But be careful! The prototype object of an instance is set when the instance is created. When JavaScript is looking up a prototype chain, what it really searches is the prototype object, not the prototype property of the constructor function. Think about the following example. Why cannot you find the property?
js> function Car() {
> Car.prototype.wheels = 4;
> }
js> function SportsCar() {
> SportsCar.prototype = new Car();
> SportsCar.prototype.doors = 2;js
> }
js> var o = { x : 10 };
js> o.propertyIsEnumerable('x');
true
js> o.propertyIsEnumerable('toString');
false
js> o.propertyIsEnumerable('xyz');
false
js> var sportsCar = new SportsCar();
js> print(sportsCar.doors);
undefined
js> print(sportsCar.wheels);
undefined
js>
> Car.prototype.wheels = 4;
> }
js> function SportsCar() {
> SportsCar.prototype = new Car();
> SportsCar.prototype.doors = 2;js
> }
js> var o = { x : 10 };
js> o.propertyIsEnumerable('x');
true
js> o.propertyIsEnumerable('toString');
false
js> o.propertyIsEnumerable('xyz');
false
js> var sportsCar = new SportsCar();
js> print(sportsCar.doors);
undefined
js> print(sportsCar.wheels);
undefined
js>
This is a common mistake made by beginners. Remind again! The prototype object of an instance is set when the instance is created. In the following code:
var sportsCar = new SportsCar();The sportsCar is specified a prototype object. That is the object referred by SportsCar.prototype right now. By default, it's an Object instance with a constructor property. After that, changing the reference of SportsCar.prototype to a Car instance will not affect the prototype object of sportsCar. The prototype object of sportsCar is still the original Object instance, not the later Car instance. It comes natural that you cannot find the doors property, not to mention the wheels property.
A real code will reveal the concept. I use the non-standard __proto__ this time.
js> function Car() {
> Car.prototype.wheels = 4;
> }
js> function SportsCar() {
> SportsCar.prototype = new Car();
> SportsCar.prototype.doors = 2;
> }
js> var p = SportsCar.prototype;
js> var sportsCar = new SportsCar();
js> p == sportsCar.__proto__;
true
js> sportsCar.__proto__ == SportsCar.prototype;
false
js>
> Car.prototype.wheels = 4;
> }
js> function SportsCar() {
> SportsCar.prototype = new Car();
> SportsCar.prototype.doors = 2;
> }
js> var p = SportsCar.prototype;
js> var sportsCar = new SportsCar();
js> p == sportsCar.__proto__;
true
js> sportsCar.__proto__ == SportsCar.prototype;
false
js>
You can see in the above example. The prototype object is set when you create an instance. In the final equality comparison, the prototype object of the instance is not the same as the object referred by SportsCar.prototype. When you use the new operator to create an object, such as:
var some = new Some();You can imagine JavaScript is doing something as follows:
var some = {};In fact, the instanceof operator also uses the prototype object of an instance to determine true or false. For example:
some.__proto__ = Some.prototype;
Some.call(some);
js> function Car() {}
js> function SportsCar() {}
js> SportsCar.prototype = new Car();
[object Object]
js> var sportsCar = new SportsCar();
js> sportsCar instanceof SportsCar;
true
js> sportsCar instanceof Car;
true
js> sportsCar instanceof Object;
true
js>
js> function SportsCar() {}
js> SportsCar.prototype = new Car();
[object Object]
js> var sportsCar = new SportsCar();
js> sportsCar instanceof SportsCar;
true
js> sportsCar instanceof Car;
true
js> sportsCar instanceof Object;
true
js>
Simply put, instanceof is based on the prototype chain. After understand this mechanism, the following code deceives instanceof with non-standard __proto__ property:
js> var o = {};
js> o instanceof Array;
false
js> o.__proto__ = Array.prototype;
js> o instanceof Array;
true
js>
js> o instanceof Array;
false
js> o.__proto__ = Array.prototype;
js> o instanceof Array;
true
js>
In the above code, the object is definitively not an Array instance but it still deceives instanceof into returning a true value.
Perhaps the prototype object is a way to inspect an instance, but __proto__ is not a standard property. If you want to check the prototype object of an instance, you can use isPrototypeOf function. In Prototype Chains, I've done the following:
js> var arr = [];
js> Array.prototype.isPrototypeOf(arr);
true
js> Function.prototype.isPrototypeOf(Array);
true
js> Object.prototype.isPrototypeOf(Array.prototype);
true
js>
js> Array.prototype.isPrototypeOf(arr);
true
js> Function.prototype.isPrototypeOf(Array);
true
js> Object.prototype.isPrototypeOf(Array.prototype);
true
js>
When JavaScript searches an object property, it looks up the prototype chain. If you want to confirm whether a property is own by an instance itself or a property of the prototype object, you can use the hasOwnProperty function. It's a property of Object.prototype so can be used through any object. For example:
js> var arr = [];
js> Array.prototype.isPrototypeOf(arr);
true
js> Object.prototype.isPrototypeOf(arr);
true
js>
js> Array.prototype.isPrototypeOf(arr);
true
js> Object.prototype.isPrototypeOf(arr);
true
js>
If the property doesn't come from the instance itself but the prototype chain, hasOwnProperty returns false. If it cannot find the property, it returns false, too.
The for in syntax can iterate user-defined properties but cannot iterate some built-in properties. If you want to know whether a property can be iterated by for in, you can use the propertyIsEnumerable function. For example:
js> var o = { x : 10 };
js> o.hasOwnPrototype('x');
js> o.hasOwnProperty('x');
true
js> o.hasOwnProperty('toString');
false
js> o.hasOwnProperty('xyz');
false
js>
js> o.hasOwnPrototype('x');
js> o.hasOwnProperty('x');
true
js> o.hasOwnProperty('toString');
false
js> o.hasOwnProperty('xyz');
false
js>
Of course, if the property does not exist, it will return false.
The ECMAScript specification says the default implementation of the toString function, defined on the Object.prototype, should return a string as follows:
'[Object class]'Basically, the built-in types of JavaScript all implement this feature. For examples, an Object instance returns '[object Object]', an Array instance returns '[object Array]', a Function instance returns '[object Function]', and so on. This can be a way to check object types. As a support to the standard specification, many libraries also use this feature to check object types.
Take care here. In Internet Explorer, typeof does not return 'function' for the alert, confirm and some built-in functions. If you use instanceof to check these functions, instanceof also returns false. The return value of toString mgith not be '[object Function]', too.