在JavaScript中,有5種基本資料型別和1種複雜資料型別,基本資料型別有:Undefined, Null, Boolean, Number和String;複雜資料型別是Object,Object中還細分了很多具體的型別,比如:Array, Function, Date等等。今天我們就來探討一下,使用什麼方法判斷一個出一個變數的型別。
在講解各種方法之前,我們首先定義出幾個測試變數,看看後面的方法究竟能把變數的型別解析成什麼樣子,以下幾個變數差不多包含了我們在實際編碼中常用的型別。
var num = 123;
var str = ‘abcdef’;
var bool = true;
var arr = [1, 2, 3, 4];
var json = {name:’wenzi’, age:25};
var func = function(){ console.log(‘this is function’); }
var und = undefined;
var nul = null;
var date = new Date();
var reg = /^[a-zA-Z]{5,20}$/;
var error= new Error();
1. 使用typeof檢測
我們平時用的最多的就是用typeof檢測變數型別了。這次,我們也使用typeof檢測變數的型別:
console.log(
typeof num,
typeof str,
typeof bool,
typeof arr,
typeof json,
typeof func,
typeof und,
typeof nul,
typeof date,
typeof reg,
typeof error
);
// number string boolean object object function undefined object object object object
從輸出的結果來看,arr, json, nul, date, reg, error 全部被檢測為object型別,其他的變數能夠被正確檢測出來。當需要變數是否是number, string, boolean, function, undefined, json型別時,可以使用typeof進行判斷。其他變數是判斷不出型別的,包括null。
還有,typeof是區分不出array和json型別的。因為使用typeof這個變數時,array和json型別輸出的都是object。
2. 使用instance檢測
在 JavaScript 中,判斷一個變數的型別嘗嘗會用 typeof 運運算元,在使用 typeof 運運算元時採用取用型別儲存值會出現一個問題,無論取用的是什麼型別的物件,它都傳回 “object”。ECMAScript 引入了另一個 Java 運運算元 instanceof 來解決這個問題。instanceof 運運算元與 typeof 運運算元相似,用於識別正在處理的物件的型別。與 typeof 方法不同的是,instanceof 方法要求開發者明確地確認物件為某特定型別。例如:
function Person(){
}
var Tom = new Person();
console.log(Tom instanceof Person);
// true
我們再看看下麵的例子:
function Person(){
}
function Student(){
}
Student.prototype = new Person();
var John = new Student();
console.log(John instanceof Student);
// true
console.log(John instancdof Person);
// true
instanceof還能檢測出多層繼承的關係。
好了,我們來使用instanceof檢測上面的那些變數:
console.log(
num instanceof Number,
str instanceof String,
bool instanceof Boolean,
arr instanceof Array,
json instanceof Object,
func instanceof Function,
und instanceof Object,
nul instanceof Object,
date instanceof Date,
reg instanceof RegExp,
error instanceof Error
)
// num : false
// str : false
// bool : false
// arr : true
// json : true
// func : true
// und : false
// nul : false
// date : true
// reg : true
// error : true
從上面的執行結果我們可以看到,num, str和bool沒有檢測出他的型別,但是我們使用下麵的方式建立num,是可以檢測出型別的:
var num = new Number(123);
var str = new String(‘abcdef’);
var boolean = new Boolean(true);
同時,我們也要看到,und和nul是檢測的Object型別,才輸出的true,因為js中沒有Undefined和Null的這種全域性型別,他們und和nul都屬於Object型別,因此輸出了true。
3. 使用constructor檢測
在使用instanceof檢測變數型別時,我們是檢測不到number, ‘string’, bool的型別的。因此,我們需要換一種方式來解決這個問題。
constructor本來是原型物件上的屬性,指向建構式。但是根據實體物件尋找屬性的順序,若實體物件上沒有實體屬性或方法時,就去原型鏈上尋找,因此,實體物件也是能使用constructor屬性的。
我們先來輸出一下num.constructor的內容,即數字型別的變數的建構式是什麼樣子的:
function Number() { [native code] }
我們可以看到它指向了Number的建構式,因此,我們可以使用num.constructor==Number來判斷num是不是Number型別的,其他的變數也類似:
function Person(){
}
var Tom = new Person();
// undefined和null沒有constructor屬性
console.log(
Tom.constructor==Person,
num.constructor==Number,
str.constructor==String,
bool.constructor==Boolean,
arr.constructor==Array,
json.constructor==Object,
func.constructor==Function,
date.constructor==Date,
reg.constructor==RegExp,
error.constructor==Error
);
// 所有結果均為true
從輸出的結果我們可以看出,除了undefined和null,其他型別的變數均能使用constructor判斷出型別。
不過使用constructor也不是保險的,因為constructor屬性是可以被修改的,會導致檢測出的結果不正確,例如:
function Person(){
}
function Student(){
}
Student.prototype = new Person();
var John = new Student();
console.log(John.constructor==Student);
// false
console.log(John.constructor==Person);
// true
在上面的例子中,Student原型中的constructor被修改為指向到Person,導致檢測不出實體物件John真實的建構式。
同時,使用instaceof和construcor,被判斷的array必須是在當前頁面宣告的!比如,一個頁面(父頁面)有一個框架,框架中取用了一個頁面(子頁面),在子頁面中宣告了一個array,並將其賦值給父頁面的一個變數,這時判斷該變數,Array == object.constructor;會傳回false;
原因:
1、array屬於取用型資料,在傳遞過程中,僅僅是取用地址的傳遞。
2、每個頁面的Array原生物件所取用的地址是不一樣的,在子頁面宣告的array,所對應的建構式,是子頁面的Array物件;父頁面來進行判斷,使用的Array並不等於子頁面的Array;切記,不然很難跟蹤問題!
4. 使用Object.prototype.toString.call
我們先不管這個是什麼,先來看看他是怎麼檢測變數型別的:
console.log(
Object.prototype.toString.call(num),
Object.prototype.toString.call(str),
Object.prototype.toString.call(bool),
Object.prototype.toString.call(arr),
Object.prototype.toString.call(json),
Object.prototype.toString.call(func),
Object.prototype.toString.call(und),
Object.prototype.toString.call(nul),
Object.prototype.toString.call(date),
Object.prototype.toString.call(reg),
Object.prototype.toString.call(error)
);
// ‘[object Number]’ ‘[object String]’ ‘[object Boolean]’ ‘[object Array]’ ‘[object Object]’
// ‘[object Function]’ ‘[object Undefined]’ ‘[object Null]’ ‘[object Date]’ ‘[object RegExp]’ ‘[object Error]’
從輸出的結果來看,Object.prototype.toString.call(變數)輸出的是一個字串,字串裡有一個陣列,第一個引數是Object,第二個引數就是這個變數的型別,而且,所有變數的型別都檢測出來了,我們只需要取出第二個引數即可。或者可以使用Object.prototype.toString.call(arr)==”object Array”來檢測變數arr是不是陣列。
我們現在再來看看ECMA裡是是怎麼定義Object.prototype.toString.call的:
Object.prototype.toString( ) When the toString method is called, the following steps are taken:
1. Get the [[Class]] property of this object.
2. Compute a string value by concatenating the three strings “[object “, Result (1), and “]”.
3. Return Result (2)
上面的規範定義了Object.prototype.toString的行為:首先,取得物件的一個內部屬性[[Class]],然後依據這個屬性,傳回一個類似於”[object Array]“的字串作為結果(看過ECMA標準的應該都知道,[[]]用來表示語言內部用到的、外部不可直接訪問的屬性,稱為“內部屬性”)。利用這個方法,再配合call,我們可以取得任何物件的內部屬性[[Class]],然後把型別檢測轉化為字串比較,以達到我們的目的。
5. jquery中$.type的實現
在jquery中提供了一個$.type的介面,來讓我們檢測變數的型別:
console.log(
$.type(num),
$.type(str),
$.type(bool),
$.type(arr),
$.type(json),
$.type(func),
$.type(und),
$.type(nul),
$.type(date),
$.type(reg),
$.type(error)
);
// number string boolean array object function undefined null date regexp error
看到輸出結果,有沒有一種熟悉的感覺?對,他就是上面使用Object.prototype.toString.call(變數)輸出的結果的第二個引數呀。
我們這裡先來對比一下上面所有方法檢測出的結果,橫排是使用的檢測方法, 豎排是各個變數:
型別判斷 | typeof | instanceof | constructor | toString.call | $.type |
num | number | false | true | [object Number] | number |
str | string | false | true | [object String] | string |
bool | boolean | false | true | [object Boolean] | boolean |
arr | object | true | true | [object Array] | array |
json | object | true | true | [object Object] | object |
func | function | true | true | [object Function] | function |
und | undefined | false | – | [object Undefined] | undefined |
nul | object | false | – | [object Null] | null |
date | object | true | true | [object Date] | date |
reg | object | true | true | [object RegExp] | regexp |
error | object | true | true | [object Error] | error |
優點 | 使用簡單,能直接輸出結果 | 能檢測出複雜的型別 | 基本能檢測出所有的型別 | 檢測出所有的型別 | – |
缺點 | 檢測出的型別太少 | 基本型別檢測不出,且不能跨iframe | 不能跨iframe,且constructor易被修改 | IE6下undefined,null均為Object | – |
這樣對比一下,就更能看到各個方法之間的區別了,而且Object.prototype.toString.call和$type輸出的結果真的很像。我們來看看jquery(2.1.2版本)內部是怎麼實現$.type方法的:
// 實體物件是能直接使用原型鏈上的方法的
var class2type = {};
var toString = class2type.toString;
// 省略部分程式碼…
type: function( obj ) {
if ( obj == null ) {
return obj + “”;
}
// Support: Android<4.0, iOS<6 (functionish RegExp)
return (typeof obj === “object” || typeof obj === “function”) ?
(class2type[ toString.call(obj) ] || “object”) :
typeof obj;
},
// 省略部分程式碼…
// Populate the class2type map
jQuery.each(“Boolean Number String Function Array Date RegExp Object Error”.split(” “), function(i, name) {
class2type[ “[object ” + name + “]” ] = name.toLowerCase();
});
我們先來看看jQuery.each的這部分:
// Populate the class2type map
jQuery.each(“Boolean Number String Function Array Date RegExp Object Error”.split(” “), function(i, name) {
class2type[ “[object ” + name + “]” ] = name.toLowerCase();
});
//迴圈之後,`class2type`的值是:
class2type = {
‘[object Boolean]’ : ‘boolean’,
‘[object Number]’ : ‘number’,
‘[object String]’ : ‘string’,
‘[object Function]’: ‘function’,
‘[object Array]’ : ‘array’,
‘[object Date]’ : ‘date’,
‘[object RegExp]’ : ‘regExp’,
‘[object Object]’ : ‘object’,
‘[object Error]’ : ‘error’
}
再來看看type方法:
// type的實現
type: function( obj ) {
// 若傳入的是null或undefined,則直接傳回這個物件的字串
// 即若傳入的物件obj是undefined,則傳回”undefined”
if ( obj == null ) {
return obj + “”;
}
// Support: Android<4.0, iOS<6 (functionish RegExp)
// 低版本regExp傳回function型別;高版本已修正,傳回object型別
// 若使用typeof檢測出的obj型別是object或function,則傳回class2type的值,否則傳回typeof檢測的型別
return (typeof obj === “object” || typeof obj === “function”) ?
(class2type[ toString.call(obj) ] || “object”) :
typeof obj;
}
當typeof obj === “object” || typeof obj === “function”時,就傳回class2type[ toString.call(obj)。到這兒,我們就應該明白為什麼Object.prototype.toString.call和$.type那麼像了吧,其實jquery中就是用Object.prototype.toString.call實現的,把'[object Boolean]’型別轉成’boolean’型別並傳回。若class2type儲存的沒有這個變數的型別,那就傳回”object”。
除了”object”和”function”型別,其他的型別則使用typeof進行檢測。即number, string, boolean型別的變數,使用typeof即可。
來自:segmentfault-wenzi
連結:http://segmentfault.com/a/1190000002962215