直入正題吧.
先看jQuery的$.isWindow函數:
. 代碼如下:
function isWin(obj){
return obj && typeof obj === 'object' && 'setInterval' in obj;
}
這個函數本身是很科學的, 它主要是通過檢查目標對象是否擁有setInterval屬性來判斷.
然而問題在於, 在缺少約定的情況下, 它也許並不太可靠, 比如:
. 代碼如下:
var o={xx:'oo'};
o['setInterval']=true;
console.log( isWin(o) ); // true
上例通過給對象字面量添加setInterval屬性, 欺詐成功.
而事實上, 任何一個非null的Object都可以如此偽裝, 比如數組:
. 代碼如下:
var arr=[1,2,3];
arr['setInterval']=true;
console.log( isWin(arr) ); // true
相比上面的屬性屬性檢查, 一個更為妥善的方法是使用對象的toString函數來判斷:
. 代碼如下:
function isWin(obj){
return Object.prototype.toString.call(obj)==='[object Window]'
}
以上函數在標准浏覽器中妥妥的, 但同時又帶來了新的兼容問題:
. 代碼如下:
// ie6-8中的結果
Object.prototype.toString.call(window)==='[object Window]'; // false
Object.prototype.toString.call(window)==='[object Object]'; // true
// chrome
Object.prototype.toString.call(window)==='[object global]'; // true
// safari
Object.prototype.toString.call(window)==='[object DOMWindow]'; // true
果然, 主要的問題又是來自萬惡的ie們. 所幸天無絕人之路, 這又讓我想起了ie中的一個靈異事件:
. 代碼如下:
// 下面兩行, 信不信?
console.log( window==document ); // true
console.log( document==window ); // false
寫到這裡, 我想最終的解決方案已經出來了:
. 代碼如下:
function isWin(obj){
return/Window|global/.test({}.toString.call(obj))||obj==obj.document&&obj.document!=obj;
}