最近閱讀了一些關於JavaScript判斷物件類型的文章。總結下來,主要有constructor屬性、typeof操作符、instanceof操作符和Object.prototype.toString()方法這四個方式來判斷對象的類型。
constructor屬性
建構函式預定義的constructor屬性是建構函式本身。
var Foo = function(){};
Foo.prototype.constructor === Foo;//true通過new調用建構函式所產生的對象以建構函式的prototype屬性為原型。雖然JavaScript中沒有類的概念,但是建構函式的作用同類的名稱相似,是物件類型的標識。訪問對象繼承的constructor屬性可以查看對象的類型。原始類型的變數也可以訪問constructor屬性,因為在訪問的時候JavaScript形成了一個封裝對象。
1 //basic objects
2 var obj = {name: "obj"};
3 obj.constructor === Object;//true
4
5 //self defined "class"
6 var Foo = function(){};
7 var f = new Foo();
8 f.constructor === Foo;//true
9
10 //primitive types
11 //Number
12 var num = 1;
13 num.constructor === Number;//true
14 var nan = NaN;
15 nan.constructor === Number;//true
16 //Boolean
17 var b = true;
18 b.constructor === Boolean;//true
19 //String
20 var s = "string";
21 s.constructor === String;//true
22 //Function
23 var Fun =function(){};
24 Fun.constructor === Function;//true;然而,constructor屬性是可以重新複製或者覆蓋的,這會引起類型判斷的錯誤。即使我們一般不會刻意去給constructor屬性賦值,但是有一些情況下constructor屬性的值和我們所期望的值不同。看下面例子:
var baseClass = function(){};
var derivedClass = function(){};
derivedClass.prototype = new baseClass();
var obj = new derivedClass();
obj.constructor === derivedClass;//false;
obj.constructor === baseClass;//true;因為子類的prototype以父類的執行個體為原型,所以通過子類執行個體訪問constructor就是父類建構函式。因此在JavaScript物件導向編程中,我們會在定義子類時加上一句代碼來糾正constructor屬性。
derivedClass.prototype.constructor = derivedClass;使用constructor進行判斷變數類型雖然方便,但是不見得特別安全,所以需要小心。
cross-frame和cross-window問題:
如果判斷來自不同frame或來自不同window的變數的對象的類型,那麼constructor屬性無法正常工作。因為不同window的核心類型不同[1]。
使用instanceof操作符
instanceof操作符判斷一個對象的原型鏈中是否存在某個建構函式的prototype屬性[2]。原型鏈的概念可以閱讀JavaScript物件導向編程(一)原型與繼承。下面的代碼形成了原型鏈obj1->derivedClass.prototype->baseClass.prototype->...->Object.prototype。Object.prototype是所有對象的原型,anyObj instanceof Object === true。
var baseClass = function(){};
var derivedClass = function(){};
derivedClass.prototype = new baseClass();//use inheritance
var obj1 = new derivedClass();
obj1 instanceof baseClass;//true
obj1 instanceof derivedClass;//true
obj1 instanceof Object;//true
obj2 = Object.create(derivedClass.prototype);
obj2 instanceof baseClass;//true
obj2 instanceof derivedClass;//true
obj2 instanceof Object;//trueconstructor屬性可以應用到除了null和undefined之外的原始類型(數字、字串、布爾類型)。而instanceof不可,但是可以使用封裝對象的方法進行判斷。
3 instanceof Number // false
'abc' instanceof String // false
true instanceof Boolean // false
new Number(3) instanceof Number // true
new String('abc') instanceof String //true
new Boolean(true) instanceof Boolean //true然而,instanceof在cross-frame和cross-window的情況下也無法正常工作。
使用 Object.prototype.toString()方法
Object.prototype.toString()方法是一個底層的方法,使用它可以返回一個字串,該字串表明了對象的類型。也可以用於判斷null和undefined。下面列出了多數常見的類型。
Object.prototype.toString.call(3);//"[object Number]"
Object.prototype.toString.call(NaN);//"[object Number]"
Object.prototype.toString.call([1,2,3]);//"[object Array]"
Object.prototype.toString.call(true);//"[object Boolean]"
Object.prototype.toString.call("abc");//"[object String]"
Object.prototype.toString.call(/[a-z]/);//"[object RegExp]"
Object.prototype.toString.call(function(){});//"[object Function]"
//null and undefined in Chrome and Firefox. In IE "[object Object]"
Object.prototype.toString.call(null);//"[object Null]"
Object.prototype.toString.call(undefined);//"[object Undefined]"
//self defined Objects
var a = new Foo();
Object.prototype.toString.call(a);//"[object Object]"
//Typed Wrappers
var b = new Boolean(true);
Object.prototype.toString.call(b);//"[object Boolean]"
var n = new Number(1);
Object.prototype.toString.call(n);//"[object Number]"
var s = new String("abc");
Object.prototype.toString.call(s);//"[object String]"經常會使用slice方法截取結果中類型的資訊:
Object.prototype.toString.call("abc").slice(8,-1);//"String"使用typeof 運算子
在MDN的一篇文檔中已經很詳細介紹了這個[3]。typeof能返回的資訊較少,有"undefined"、"object"、"boolean"、"number"、"string"、"function"、"xml"這幾種。
Type Result
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Host object (provided by the JS environment) Implementation-dependent
Function object (implements [[Call]] in ECMA-262 terms) "function"
E4X XML object "xml"
E4X XMLList object "xml"
Any other object "object"
// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // Despite being "Not-A-Number"
typeof Number(1) === 'number'; // but never use this form!
// Strings
typeof "" === 'string';
typeof "bla" === 'string';
typeof (typeof 1) === 'string'; // typeof always return a string
typeof String("abc") === 'string'; // but never use this form!
// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(true) === 'boolean'; // but never use this form!
// Undefined
typeof undefined === 'undefined';
typeof blabla === 'undefined'; // an undefined variable
// Objects
typeof {a:1} === 'object';
typeof [1, 2, 4] === 'object'; // use Array.isArray or Object.prototype.toString.call to differentiate regular objects from arrays
typeof new Date() === 'object';
typeof new Boolean(true) === 'object'; // this is confusing. Don't use!
typeof new Number(1) === 'object'; // this is confusing. Don't use!
typeof new String("abc") === 'object'; // this is confusing. Don't use!
// Functions
typeof function(){} === 'function';
typeof Math.sin === 'function';
typeof undefined;//"undefined"
typeof null;//"object" This stands since the beginning of JavaScript
typeof /s/ === 'object'; // Conform to ECMAScript 5.1typeof 封裝對象的結果是‘object’需要注意。這裡不評價好與不好(如果需要區分封裝對象和原始類型呢)。但是typeof不是一個健壯的方法,要小心使用。比如:
var s = "I am a string";
typeof s === "string";
//Add a method to String
String.prototype.A_String_Method = function(){
console.log(this.valueOf());
console.log(typeof this);
};
s.A_String_Method();
//I am a string
//object