本來這應該是最簡單的事情,Javascript中提供了instanceof運算符,可以檢測某個變量是否某種類型的實例,一般情況下可以這樣檢測數組:testVar instanceof Array == true。但是,在跨frame的時候,等式不成立。假設測試頁test.html的代碼如下:
<script language=”javascript” type=”text/javascript”>
//<![CDATA[
function isArray(testVar) {
return testVar instanceof Array;
}
//]]>
</script>
<iframe id=”testFrame” src=”testframe.html”></iframe>
testframe.html的代碼如下:
<script language=”javascript” type=”text/javascript”>
//<![CDATA[
function isArray(testVar) {
alert(parent.isArray([]));
}
//]]>
</script>
輸出的內容是false。似乎每個頁面都有自己的Array類型,如果把isArray改寫一下,輸出的就是true:
function isArray(testVar) {
return testVar instanceof document.getElementById(”testFrame”).contentWindow.Array;
}
檢測testVar.constructor也會出現類似的情況。因此,這種方法不可行。
通過數組獨有的函數進行檢測,比如檢測testVar.sort是否未定義。這種方法在一般情況下也是可行的,但是健壯性不足。如果給testVar動態加了一個sort方法,判斷就會失誤。
沒什麼好說的,直接看代碼,太牛了:
if (Object.prototype.toString.call(testVar) === “[object Array]“) return 1;
所謂的集合就是可以通過下標訪問但又不是數組的類型。已知的Javascript集合有兩種,一種是HtmlCollection,另一種是函數的參數arguments。
在已知testVar不是數組的情況下,先檢測它的length屬性是否存在。包含length屬性的類型也不少,比如window、String、某些HtmlElement。所以要檢測的特征非常多:
testVar.length != null &&
!testVar.alert && // 不是window
!testVar.charAt && // 不是String
!testVar.nodeType // 不是HtmlElement
由於其他情況實在太多,容易出現疏漏,所以最終還是沒有采取這種辦法。
已知的集合只有兩種,所以還是檢查這兩種集合的特性吧。HtmlCollection有item方法,而arguments則有callee屬性:
if (testVar.item || testVar.callee) return 2;
這時,select元素開始攪局。它竟然包含HtmlCollectiond的所有特性。於是,還是要判斷nodeType:
if (!testVar.nodeType && testVar.item || testVar.callee) return 2;
select元素被轟走了,萬惡的ie開始搗亂。首先是XML的問題,某個ajax回調函數:
function onSuccess(xhr) {
var xmlDoc = xhr.responseText;
alert(xmlDoc.getElementsByTagName); // ie下報錯
var root = xmlDoc.getElementsByTagName(”root”);
alert(root.item) // ie下報錯
}
也就是說,在ie下,只要嘗試檢測xml節點或xml節點集合的方法都會報錯。幸好還可以用typeof去對付它們。
function onSuccess(xhr) {
var xmlDoc = xhr.responseText;
alert(typeof(xmlDoc.getElementsByTagName)); // ie下輸出”unknown”
var root = xmlDoc.getElementsByTagName(”root”);
alert(typeof(root.item)) // ie下輸出”unknown”
}
因此,代碼就改成:
if (!testVar.nodeType && typeof testVar.item != “undefined” || testVar.callee) return 2;
其次,是window對象的問題:ie下的window對象也有item方法。所以還是要檢測window對象:
if (!testVar.nodeType && typeof testVar.item != “undefined” && !testVar.alert || testVar.callee) return 2;
雖然檢測特性容易出現失誤,但是目前也只有這種辦法了。
至此,終於折騰完,整個函數簡寫後就是:
var isArray = function(testVar) {
return Object.prototype.toString.call(testVar) === “[object Array]” ? 1 : testVar.callee || (typeof testVar.item != “undefined” && !testVar.nodeType && !testVar.alert) ? 2 : 0;
};
目前還不知道有沒有疏漏。