在判斷函數類型時,我們通常會使用typeof方法,一般情況下,它會得到我們所預想的效果。但是,有一些細節並不為我們所熟知。 John Resig 在分析了這些細節之後,為我們提供了一個完美的解決方案,本文將作詳細介紹:
一、傳統方法不為人知的細節
毫無疑問,在判斷函數類型時,我們使用的是typeof方法,例如:
function fn(){
//content
}
alert(typeof fn)//結果是"function"。
但是,該方法在一些瀏覽器中並不是像我們想像的那樣運作。
1、Firefox2和Firefox3
在這兩個瀏覽器中,用typeof檢測HTML物件元素的類型,得到是一個不精確的“function”結果,而不是“object”,如HTMLDocument。如:
alert(typeof HTMLDocument);
//在Firefox2中結果是"function";
//在Firefox3中結果是"object";
2、Firefox2
對於正規表示式,在該瀏覽器中傳回的結果是「function」(在Firefox3中結果是「object」),如:
var reg = /test/;
alert(typeof reg);
//在Firefox2中結果是"function";
//在Firefox3中結果是"object";
註:本人在safari中測試,其結果也是「function」。
3、IE6和IE7
在IE中對DOM元素使用typeof方法,得到的結果是「object」。如:
alert(typeof document.getElementsByTagName("body")[0].getAttribute);
//結果是"object"
4、Safari 3
safari認為DOM元素的NodeList是個函數,如:
alert(typeof document.body.childNodes);
//結果是"function"
很明顯,如果你要測試一個物件是否為函數,使用typeof方法並不能從真正意義上保證測試結果。那麼,我們需要一個在所有瀏覽器中都能保證測試結果的解決方案。我們知道function本身有apply()和call()兩種方法,但這兩個方法在IE中有問題的函數中並不存在,試試看下面的測試:
alert(typeof document.getElementsByTagName("body")[0].getAttribute.call)
//在IE中結果是"undefined"
顯然,我們不能利用這兩個方法。
二、完美解決方案及實現流程
John Resig為我們提供了一個完美的解決方案,這個複雜但很穩定的判斷一個物件是否為函數的方法如下:
function isFunction( fn ) {
return !!fn && !fn.nodeName && fn.constructor != String &&
fn.constructor != RegExp && fn.constructor != Array &&
/function/i.test( fn + "" );
}
這個函數先保證測試的物件存在,並將其序列化成含有「function」的字串,而這個是我們偵測的基礎(fn.constructor != String,fn.constructor != Array, and fn.constructor != RegExp )。另外,我們需要保證宣告的函數不是一個DOM節點(fn.nodeName)。然後,我們就可以作toString測試。如果我們將一個函數轉換成字串,在一個瀏覽器中(fn+"")給我們的結果就像這樣「function name(){...}」。現在,判斷它是否為函數就很簡單,僅只需要判斷字串中是否包含單字「function」。這很神奇,對於任何有問題的函數,在所有瀏覽器中都能得到我們所需的結果。這個函數較之於傳統的方法,運行速度有些不盡人意,作者建議我們保守使用。
John Resig是jQuery函式庫的開發者,相信使用該函式庫的朋友們對該函式庫簡潔的語法和優秀的效能並不陌生。作者除追求程式碼簡潔和效能高效之外,其盡善盡美的精神也讓人嘆服。如果你是個完美主義者,相信此文對你很有幫助。