• JavaScript 类型判断 —— typeof 以及 instanceof 中的陷阱


    JavaScript中基本类型包含Undefined、Null、Boolean、Number、String以及Object引用类型。
    基本类型可以通过typeof来进行检测,对象类型可以通过instanceof来检测。
    但这两检测方式本身存在大量的陷阱,因此需要进行兼容处理。

    对于typeof,只能识别出undefined、object、boolean、number、string、function这6种数据类型,无法识别Null等细分的对象类型。
    typeof本身存在的陷阱:
    typeof null; 结果为"object"
    typeof document.all; 在IE外的其他现代浏览器下会返回"undefined",但实际上是可用的(该方法被大量用作判断IE,因此浏览器厂商也有对应规则)。
    typeof document.childNodes; 在safari下结果为"function"
    typeof document.createElement('embed'); 在firefox下结果为"function"
    typeof document.createElement('object'); 在firefox下结果为"function"
    typeof document.createElement('applet'); 在firefox下结果为"function"
    typeof window.alert; 在IE678下为"object",
    IE678下有个HACK技巧:window==document 在IE678下为true(而document==window为false)。

    对于typeof,在IE下判断ActiveX对象的方法时还会返回unknow的情况,例如:

     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5 <style rel="stylesheet" type="text/css">
     6 </style>
     7 <script>
     8 window.onload=function(){
     9     if(window.ActiveXObject){
    10         var xhr=new ActiveXObject("Msxml2.XMLHTTP");
    11         document.body.innerHTML=(typeof xhr.abort);
    12     }
    13 }
    14 </script>
    15 </head>
    16 <body><div class="show">HELLO</div></body>
    17 </html>
    typeof在IE下判断ActiveX方法的时候会返回unknow

    对于如上的这个IE特性,可以用来判断VBScript方法是否存在:

     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5 <style rel="stylesheet" type="text/css">
     6 </style>
     7 <script type="text/VBScript">
     8 function vbf(a,b)
     9 vbf = a+b
    10 end function
    11 </script>
    12 <script type="text/javascript">
    13 window.onload=function(){
    14     // 如下在IE下为"unknow"
    15     document.body.innerHTML=(typeof vbf);
    16 }
    17 </script>
    18 </head>
    19 <body><div class="show">HELLO</div></body>
    20 </html>
    判断VBScript方法

    对于instanceof,原型上存在此对象的构造器就会返回true,但有如下陷阱:
    跨文档的iframe里的数组实例不是父窗口的Array的实例:

     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5 <style rel="stylesheet" type="text/css">
     6 </style>
     7 <script>
     8 window.onload=function(){
     9     document.body.appendChild(document.createElement("iframe"));
    10     var frame=window.frames[window.frames.length-1];
    11     // IE678无法获取
    12     if("Array" in frame){
    13         var fArray = frame.Array;
    14         var a=new fArray(1);
    15         alert(a instanceof Array); // false
    16         alert(a.constructor == Array); // false
    17     }
    18 }
    19 </script>
    20 </head>
    21 <body><div class="show">HELLO</div></body>
    22 </html>
    测试跨文档Array实例的类型判断

    对于对象的constructor方法的陷阱:
    在IE67下window.constructor、document.constructor等BOM与DOM对象的constructor属性是未暴露的。
    在IE6789下ActiveXObject对象的constructor方法也是未暴露的。

    通过如上的分析,先定义一个对象用于内建对象的字符串名称的类型映射:

     1     var class2type={
     2         "[object Array]":"array",
     3         "[object Boolean]":"boolean",
     4         "[object Date]":"date",
     5         "[object Function]":"function",
     6         "[object Number]":"number",
     7         "[object Object]":"object",
     8         "[object RegExp]":"regexp",
     9         "[object String]":"string"
    10     };

    然后通过Object.prototype.toString这个方法来输出对象内部对应的字符串名称来判断:

    1     function type(obj){
    2         return obj == null ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
    3     }

    对于数字的判断,首先要判断是否为数字类型(isNaN),其次还要判断是否为数字中的极值(isFinite):

    1 // 只要可以转换为运算数字.
    2 function isNumberic(obj){
    3     return !isNaN(parseFloat(obj)) && isFinite(obj);
    4 }

    在Jquery2.1中,对数字的判断更加精简,如下所示:

    1 function isNumberic(obj){
    2     return obj - parseFloat(obj) >= 0;
    3 }

    对于window对象的判断,在JQuery 1.7.2中判断比较简单,通过检查obj.window==obj来判断,
    如果使用Object.prototype.toString,因为window对象是宿主对象,非ECMA规范对象,因此返回的字符串名可能如下:
    IE678      返回[object Object]
    IE9、FF   返回[object Window]
    Chrome   返回[object global]
    Safari      返回[object DOMWindow]
    再通过如上的对于IE678的 (obj===obj.document&&obj.document!==obj) 来判断IE678下的window对象,最后实现如下:

    1 // 通过Object.prototype.toString判断,但IE678下通过HACK判断.
    2 function isWindow(obj){
    3     return obj != null && (/Window|global/.test(Object.prototype.toString.call(obj))||(obj==obj.document&&obj.document!=obj));
    4 }

    另外通常需要实现一个方法,判断一个参数是否为字面量{}创建的对象或new Object()创建的对象,
    需要先排除原生对象,排除DOM对象,排除window对象,
    再需要判断对象的原型是否存在isPrototypeOf方法,不存在也排除(注意捕获异常)。

    综上所述,整理后的代码如下:

     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5 <style rel="stylesheet" type="text/css">
     6 </style>
     7 <script type="text/javascript">
     8 var class2type={
     9     "[object Array]":"array",
    10     "[object Boolean]":"boolean",
    11     "[object Date]":"date",
    12     "[object Function]":"function",
    13     "[object Number]":"number",
    14     "[object Object]":"object",
    15     "[object RegExp]":"regexp",
    16     "[object String]":"string"
    17 };
    18 function type(obj){
    19     return obj == null ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
    20 }
    21 function isFunction(obj){
    22     // 这样判断解决了FF下对embed、object、applet的typeof返回"function"的陷阱.
    23     return type(obj) === "function";
    24 }
    25 function isArray(obj){
    26     // 解决了instanceof Array不能对iframe中的Array对象正确判断的陷阱.
    27     return type(obj) === "array";
    28 }
    29 function isNaN(obj){
    30     return obj !== obj;
    31 }
    32 function isNull(obj){
    33     return obj === null;
    34 }
    35 function isNull2(obj){
    36     return type(obj) === "null";
    37 }
    38 function isUndefined(obj){
    39     return obj === void 0;
    40 }
    41 function isUndefined2(obj){
    42     return type(obj) === "undefined";
    43 }
    44 // 只要可以转换为运算数字.
    45 function isNumberic(obj){
    46     return !isNaN(parseFloat(obj)) && isFinite(obj);
    47 }
    48 // Number.NEGATIVE_INFINITY-Number.NEGATIVE_INFINITY为NaN,经过判断后返回false.
    49 function isNumberic2(obj){
    50     return obj - parseFloat(obj) >= 0;
    51 }
    52 // 通过Object.prototype.toString判断,但IE678下通过HACK判断.
    53 function isWindow(obj){
    54     return obj != null && (/Window|global/.test(Object.prototype.toString.call(obj))||(obj==obj.document&&obj.document!=obj));
    55 }
    56 function isPlainObject(obj){
    57     if(!obj||type(obj)!=="object"||obj.nodeType||isWindow(obj)){
    58         return;
    59     }
    60     try{
    61         // 存在构造函数但最近的原型对象中不存在isPrototypeOf这个Object原型特有属性。
    62         if(obj.constructor&&!Object.prototype.hasOwnProperty.call(obj.constructor.prototype,"isPrototypeOf")){
    63             return false;
    64         }
    65     }
    66     catch(e){
    67         // IE678都有可能在这里抛出异常,当obj为一些没有暴露constructor的原生对象的时候.
    68         return false;
    69     }
    70     var key;
    71     for(key in obj){}
    72         // for in 循环先枚举非继承属性,再枚举继承属性.
    73         // 如果对象的最后一个属性是非继承属性,那么所有属性都是非继承属性.
    74     return key === undefined || Object.prototype.call(obj,key);
    75 }
    76 window.onload=function(){
    77     /* 进行一些测试 */
    78     var br="<br/>"
    79     var nframe=document.body.appendChild(document.createElement("iframe"));
    80     var frame=window.frames[window.frames.length-1];
    81     nframe.style.display="none";
    82     // IE678无法获取
    83     if("Array" in frame){
    84         var fArray = frame.Array;
    85         var a=new fArray(1);
    86         document.body.innerHTML += "instanceof判断Array在跨框架下的结果:" + (a instanceof Array) + br;  // false.
    87         document.body.innerHTML += "通过原型的toString方法判断的结果:" + isArray(a) + br;  // true.
    88     }
    89     document.body.innerHTML += "window:" + isWindow(window) + br;  // true.
    90     document.body.innerHTML += "new Object:" + isPlainObject({}) + br;
    91 }
    92 </script>
    93 </head>
    94 <body><div class="show">HELLO</div></body>
    95 </html>



  • 相关阅读:
    锁表
    vs2010宏的应用
    Oracle:sqlplus查询出的中文是乱码问题的解决(转)
    linux下查看端口和服务的一些命令
    netsh
    xpshutdown关机命令详解
    linux系统如何察看、修改系统时间
    delphi编程中调用其他执行程序?(转)
    如何修改Oracle用户密码的诀窍(转)
    转:用Delphi实现从Excel数据Update(Insert类似)到Oracle数据库表中
  • 原文地址:https://www.cnblogs.com/xf_z1988/p/javascript_type_check.html
Copyright © 2020-2023  润新知