- 相關(guān)推薦
如何檢查JavaScript變量的類(lèi)型
JavaScript基本數據類(lèi)型有5種:字符串、數字、布爾、null、undefined。 用戶(hù)定義的類(lèi)型(object)并沒(méi)有類(lèi)的聲明,因此繼承關(guān)系只能通過(guò)構造函數和原型鏈來(lái)檢查。 如何檢查一個(gè)變量的類(lèi)型?以下僅供參考!
先給結論:
如果你要判斷的是基本數據類(lèi)型或JavaScript內置對象,使用toString; 如果要判斷的時(shí)自定義類(lèi)型,請使用instanceof。
不同的編程語(yǔ)言都有自己的方式來(lái)提供類(lèi)型信息,例如C#的反射、C++的Traits, JavaScript提供類(lèi)型信息的方式更加靈活,因而也容易產(chǎn)生很多誤用。 下面來(lái)分析常見(jiàn)類(lèi)型檢查手段的區別:typeof, instanceof, constructor, toString。
如果你在尋找類(lèi)型轉換的解決方案,而非類(lèi)型檢查,請移步JavaScript類(lèi)型轉換。
typeof
typeof 操作符返回的是類(lèi)型字符串,它的返回值有6種取值:
typeof 3 // "number"
typeof "abc" // "string"
typeof {} // "object"
typeof true // "boolean"
typeof undefined // "undefined"
typeof function(){} // "function"
所有對象的typeof都是"object",不能用于檢測用戶(hù)自定義類(lèi)型。 比如Date, RegExp, Array, DOM Element的類(lèi)型都是"object":
typeof [] // "object"
typeof還有一個(gè)知名的bug:
typeof null // "object"
null是基本數據類(lèi)型,它的類(lèi)型顯然是Null。其實(shí)這也反映了null的語(yǔ)義, 它是一個(gè)空指針表示對象為空,而undefined才表示什么都沒(méi)有。 總之,typeof只能用于基本數據類(lèi)型檢測,對于null還有Bug。
instanceof
instanceof操作符用于檢查某個(gè)對象的原型鏈是否包含某個(gè)構造函數的prototype屬性。例如:
obj instanceof Widget
obj的原型鏈上有很多對象(成為隱式原型),比如:obj.__proto__, obj.__proto__.__proto__, ...如果這些對象里存在一個(gè)p === Widget.prototype,那么instanceof結果為true,否則為false。
instanceof是通過(guò)原型鏈來(lái)檢查類(lèi)型的,所以適用于任何"object"的類(lèi)型檢查。
// 比如直接原型關(guān)系
function Animal(){ }
(new Animal) instanceof Animal // true
// 原型鏈上的間接原型
function Cat(){}
Cat.prototype = new Animal
(new Cat) instanceof Animal // true
instanceof也可以用來(lái)檢測內置兌現,比如Array, RegExp, Object, Function:
[1, 2, 3] instanceof Array // true
/abc/ instanceof RegExp // true
({}) instanceof Object // true
(function(){}) instanceof Function // true
instanceof對基本數據類(lèi)型不起作用,因為基本數據類(lèi)型沒(méi)有原型鏈。
3 instanceof Number // false
true instanceof Boolean // false
'abc' instanceof String // false
null instanceof XXX // always false
undefined instanceof XXX // always false
但你可以這樣:
new Number(3) instanceof Number // true
new Boolean(true) instanceof Boolean // true
new String('abc') instanceof String // true
但這時(shí)你已經(jīng)知道數據類(lèi)型了,類(lèi)型檢查已經(jīng)沒(méi)有意義了。
constructor
constructor屬性返回一個(gè)指向創(chuàng )建了該對象原型的函數引用。需要注意的是,該屬性的值是那個(gè)函數本身。例如:
function Animal(){}
var a = new Animal
a.constructor === Animal // true
constructor不適合用來(lái)判斷變量類(lèi)型。首先因為它是一個(gè)屬性,所以非常容易被偽造:
var a = new Animal
a.constructor === Array
a.constructor === Animal // false
另外constructor指向的是最初創(chuàng )建當前對象的函數,是原型鏈最上層的那個(gè)方法:
function Cat(){}
Cat.prototype = new Animal
function BadCat(){}
BadCat.prototype = new Cat
(new BadCat).constructor === Animal // true
Animal.constructor === Function // true
與instanceof類(lèi)似,constructor只能用于檢測對象,對基本數據類(lèi)型無(wú)能為力。 而且因為constructor是對象屬性,在基本數據類(lèi)型上調用會(huì )拋出TypeError異常:
null.constructor // TypeError!
undefined.constructor // TypeError!
與instanceof不同的是,在訪(fǎng)問(wèn)基本數據類(lèi)型的屬性時(shí),JavaScript會(huì )自動(dòng)調用其構造函數來(lái)生成一個(gè)對象。例如:
(3).constructor === Number // true
true.constructor === Boolean // true
'abc'.constructor === String // true
// 相當于
(new Number(3)).constructor === Number
(new Boolean(true)).constructor === Boolean
(new String('abc')).constructor === String
這種將一個(gè)值類(lèi)型轉換為對象引用類(lèi)型的機制在其他語(yǔ)言中也存在,在C#中稱(chēng)為裝箱(Boxing)。
跨窗口問(wèn)題
我們知道Javascript是運行在宿主環(huán)境下的,而每個(gè)宿主環(huán)境會(huì )提供一套ECMA標準的內置對象,以及宿主對象(如window, document),一個(gè)新的窗口即是一個(gè)新的宿主環(huán)境。 不同窗口下的內置對象是不同的實(shí)例,擁有不同的內存地址。
而instanceof和constructor都是通過(guò)比較兩個(gè)Function是否相等來(lái)進(jìn)行類(lèi)型判斷的。 此時(shí)顯然會(huì )出問(wèn)題,例如:
var iframe = document.createElement('iframe');
var iWindow = iframe.contentWindow;
document.body.appendChild(iframe);
iWindow.Array === Array // false
// 相當于
iWindow.Array === window.Array // false
因此iWindow中的數組arr原型鏈上是沒(méi)有window.Array的。請看:
iWindow.document.write('
');
iWindow.arr instanceof Array // false
iWindow.arr instanceof iWindow.Array // true
toString
toString方法是最為可靠的類(lèi)型檢測手段,它會(huì )將當前對象轉換為字符串并輸出。 toString屬性定義在Object.prototype上,因而所有對象都擁有toString方法。 但Array, Date等對象會(huì )重寫(xiě)從Object.prototype繼承來(lái)的toString, 所以最好用Object.prototype.toString來(lái)檢測類(lèi)型。
toString = Object.prototype.toString;
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
toString.call(3); // [object Number]
toString.call([]); // [object Array]
toString.call({}); // [object Object]
// Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
toString也不是完美的,它無(wú)法檢測用戶(hù)自定義類(lèi)型。 因為Object.prototype是不知道用戶(hù)會(huì )創(chuàng )造什么類(lèi)型的, 它只能檢測ECMA標準中的那些內置類(lèi)型。
toString.call(new Animal) // [object Object]
因為返回值是字符串,也避免了跨窗口問(wèn)題。當然IE彈窗中還是有Bug,不必管它了。 現在多少人還在用IE?多少人還在用彈窗?
和Object.prototype.toString類(lèi)似地,Function.prototype.toString也有類(lèi)似功能, 不過(guò)它的this只能是Function,其他類(lèi)型(例如基本數據類(lèi)型)都會(huì )拋出異常。
總結
typeof只能檢測基本數據類(lèi)型,對于null還有Bug;
instanceof適用于檢測對象,它是基于原型鏈運作的;
constructor指向的是最初創(chuàng )建者,而且容易偽造,不適合做類(lèi)型判斷;
toString適用于ECMA內置JavaScript類(lèi)型(包括基本數據類(lèi)型和內置對象)的類(lèi)型判斷;
基于引用判等的類(lèi)型檢查都有跨窗口問(wèn)題,比如instanceof和constructor。
總之,如果你要判斷的是基本數據類(lèi)型或JavaScript內置對象,使用toString; 如果要判斷的時(shí)自定義類(lèi)型,請使用instanceof。
有時(shí)Duck Typing的方式也非?尚,貌似已經(jīng)成為了前端的慣例。 比如jQuery是這樣判斷一個(gè)Window的:
isWindow: function(obj){
return obj && typeof obj === 'object' && "setInterval" in obj;
}
另外DOM Element的類(lèi)型檢測也可以通過(guò)上述的方法來(lái)完成,但沒(méi)有一種方法在任何瀏覽器上都可行。 DOM Element的類(lèi)型檢測可以參見(jiàn)這篇文章:http://tobyho.com/2011/01/28/checking-types-in-javascript/
同時(shí)發(fā)表在:http://harttle.com/2015/09/18/js-type-checking.html
【如何檢查JavaScript變量的類(lèi)型】相關(guān)文章:
Java 變量類(lèi)型10-01
Java 變量類(lèi)型介紹10-17
C語(yǔ)言變量的類(lèi)型10-30
C語(yǔ)言指針變量的類(lèi)型07-31
Java的變量類(lèi)型有哪些06-30
C語(yǔ)言變量和數據類(lèi)型08-06
Java數據類(lèi)型以及變量的定義10-12