Javascript類型檢測

作者: 笨蛋的座右銘  來源: 博客園  發布時間: 2010-10-12 14:30  閱讀: 956 次  推薦: 0   原文鏈接   [收藏]  

  開門見山,我們先來看一下代碼:

 
var is = function (obj,type) {
var toString = Object.prototype.toString;
var _baseTypes = {'undefined':'undefined','number':'number','boolean':'boolean','string':'string'};
return (_baseTypes[typeof obj]===type)||
(type === "Null" && obj === null) ||
(type==='Function'&&"object" === typeof document.getElementById ?
/^\s*\bfunction\b/.test("" + obj):toString.call(obj).slice(8,-1) === type)||
obj instanceof type;
};

  在講解這個函數之前,讓我們先來看一下javascript類型判斷的幾個關鍵:

  typeof

  typeof運算符,用于判斷一個或值是哪種類型的。

  由于Javascript語言的靈性性與執行環境的復雜性,typeof運算符并不能正確判斷出所有的類型。但對于基本數據類型,typeof具有神奇的功效,正如在 is 函數中看到的那樣,_baseTypes變量中定義了typeof可以正確識別的基本數據類型列表,其中鍵為類型名稱,值為typeof運算結果。

  instanceof

  instanceof可以檢測某個對象是不是另一個對象的實例。

 
new String('abc') instanceof String //true

  instanceof還可以檢測父類型。 

 
function Animal() {};
function Pig() {};
Pig.prototype
= new Animal();
alert(
new Pig() instanceof Animal); // true

  可以看出,instanceof不適合用來檢測一個對象本身的類型。

  Constructor

  所有對象都擁有一個constructor屬性,它指向該對象的構造函數。對于復合數據類型,我們可以采用如下方式進行檢測:

 
isArray: function(arr) {
return !!arr && arr.constructor == Array;
}

  但是比較悲劇的是這種行為并不是確定的,在一些極其特殊的情況下,會產生意外的結果:

 
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);

var xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1, 2, 3); // [1,2,3]
alert(arr instanceof Array); //false

  下面是Johg Resig 《pro javacript Techniques》書中的一張列表:

  Duck Typing 

  在犀牛書里,提到一句老話:"如果走路像鴨子,叫聲像鴨子,那他就是鴨子"。換而言之,對于Array來說,如果一個對象有splice和join屬性,那它就是一個Array:

 
function isArray(o) {
return o != null && typeof o === ‘object’ &&
'splice' in o && 'join' in o;
}

  顯然,鴨子檢測很容易誤把自造的天鵝也當成鴨子:

 
alert(isArray({'splice': '', 'join': ''})); // true

  注:buck typing并不關注于類型,它關注的是行為,它是實際作用是相互之間約定一種行為接口,好實現類似于多態的調用

  Object.toString

   ECMA-262中的解釋:

  Object.prototype.toString()

  when the toString method is called,the following steps are taken:

  1.get the [[Class]] property of this object  //得到內部屬性[[Class]]

  2.conpute a string value by concatenating the three strings "[object",Result(1),and"]" //構造一個字符串類型似于[object xxx]

  3.return results(2) //返回第2條的執行結果

  注:[[Class]]為對象的內部屬性,無法真接訪問到,這樣,就有了:

 
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]';
}

  因為是字符串比較,也就解決了環境問題和語法靈活性所帶來的變化,因此比較穩定。好,講到現在,想必大家對javascript類型判斷應該有一個較為深入的理解,我想通過自已的能力,應該也可以寫一個比較完善的類型判斷函數,那下面就來看一下我是如何實現的。

 
var is = function (obj,type) {
var toString = Object.prototype.toString;
var _baseTypes = {'undefined':'undefined','number':'number','boolean':'boolean','string':'string'};
return (_baseTypes[typeof obj]===type)||
(type === "Null" && obj === null) ||
(type==='Function'&&"object" === typeof document.getElementById ?
/^\s*\bfunction\b/.test("" + obj):toString.call(obj).slice(8,-1) === type)||
obj instanceof type;
};

  因為考慮到實用性,這里是通過傳入對象obj和期望類型type,返回boolean值,true為obj為type類型,false為obj不為type類型來實現的。首先,var toString = Object.prototype.toString;這里保存了一份對Object的原生toString方法的引用,方便后面使用:

 
var _baseTypes = {'undefined':'undefined','number':'number','boolean':'boolean','string':'string'};

  這里保存了一份對于typeof可以檢測出來的基本數據類型的對象列表,其鍵類型名稱,值為typeof該類型的結果。然后:進行類型的檢測,返回結果。

  (_baseTypes[typeof obj] === type) :檢測是否為基本數據類型

  (type === 'Null' && obj === null):因為null實際上屬于Object類型,因此typeof null 和Object.prototype.toString(null)返回的結果都為object和[Object object]

  在實際需求中,我們通常希望將null單獨列出來作為一種類型來進行判斷:

 
(type==='function'&&'object'===typeof document.getElementById?/^\s*\bfunction\b/.test(""+obj):toString.call(obj).slice(8,-1)===type)

  這里實際上是在判斷obj是否是一個函數,而IE6存在bug,無法正確識別getElementById這類函數,因此做了上些特殊的處理。

  obj instanceof type:判斷obj是否為type的實例

  這里主要是來處理一引起特殊情況,如一些自定義對象的問題。

 
function Animal() {}
function SubArray() {}
SubArray.prototype
= [];

var toString = Object.prototype.toString;
alert(toString(
new Animal()));
alert(toString(
new SubArray()));
// firefox: [object Window]
// ie: [object Object]
// chrome: [object global]

alert(new SubArray() instanceof Array); // true
alert(new Animal() instanceof Animal); // true

  好,并不多了,最后我們來看一個測試結果:

 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>typecheck.html</title>

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
<script type="text/javascript">
var is = function (obj,type) {
var toString = Object.prototype.toString;
var _baseTypes = {'undefined':'undefined','number':'number','boolean':'boolean','string':'string'};
return (_baseTypes[typeof obj]===type)||
(type === "Null" && obj === null) ||
(type==='Function'&&"object" === typeof document.getElementById ?
/^\s*\bfunction\b/.test("" + obj):toString.call(obj).slice(8,-1) === type)||
obj instanceof type;
};

window.onload
= function(){
var msg = document.getElementById("msg");
function addMsg(m){
msg.innerHTML
+=m+"<br />";
}


//判斷基本數據類型
addMsg("1,number"+":"+is(1,'number'));
addMsg(
"abc,string"+":"+is("abc","string"));
addMsg(
"true,boolean"+":"+is(true,"boolean"));
addMsg(
"undefined,undefined"+":"+is(undefined,'undefined'));
//判斷引用數據類型
addMsg("null,Null"+":"+is(null,"Null"));
addMsg(
"new String(''),String"+":"+is(new String(""),"String"));
addMsg(
"{},Object"+":"+is({},"Object"));
addMsg(
"[],Array"+":"+is([],"Array"));
addMsg(
"/foo/,RegExp"+":"+is(/foo/,"RegExp"));
try {0()} catch (e) {
addMsg(
"try/catch(e),Error"+":"+is(e,"Error"));
}
addMsg(
"new Date(),Date"+":"+is(new Date(),"Date"));
addMsg(
"new Number(123),Number"+":"+is(new Number(123),"Number"));
addMsg(
"new Boolean(true),Boolean"+":"+is(new Boolean(true),"Boolean"));
addMsg(
"function(){},Function"+":"+is(function(){},"Function"));

function SubArray() {}
SubArray.prototype
= [];
addMsg(
"SubArray,Array"+":"+is(new SubArray(),Array));
}


</script>
</head>

<body>
<div id="msg"></div>
</body>
</html>

  兼容判斷類型列表:

  基本數據類型 undefined,string,number,boolean

  復合數據類型 Date,String,Boolean,Number,Object,Function,Array,RegExp,Error

  其他 instanceof 的范疇,參考:

  http://lifesinger.org/blog/2009/02/javascript-type-check-1/
  http://www.planabc.net/2010/01/23/repair_the_bug_of_isfunction_method_in_jquery/

0
0
 
標簽:Javascript
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()