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的情况,例如:

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style rel="stylesheet" type="text/css">
</style>
<script>
window.onload=function(){
if(window.ActiveXObject){
var xhr=new ActiveXObject("Msxml2.XMLHTTP");
document.body.innerHTML=(typeof xhr.abort);
}
}
</script>
</head>
<body><div class="show">HELLO</div></body>
</html>

typeof在IE下判断ActiveX方法的时候会返回unknow

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

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style rel="stylesheet" type="text/css">
</style>
<script type="text/VBScript">
function vbf(a,b)
vbf = a+b
end function
</script>
<script type="text/javascript">
window.onload=function(){
// 如下在IE下为"unknow"
document.body.innerHTML=(typeof vbf);
}
</script>
</head>
<body><div class="show">HELLO</div></body>
</html>

判断VBScript方法

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

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style rel="stylesheet" type="text/css">
</style>
<script>
window.onload=function(){
document.body.appendChild(document.createElement("iframe"));
var frame=window.frames[window.frames.length-1];
// IE678无法获取
if("Array" in frame){
var fArray = frame.Array;
var a=new fArray(1);
alert(a instanceof Array); // false
alert(a.constructor == Array); // false
}
}
</script>
</head>
<body><div class="show">HELLO</div></body>
</html>

测试跨文档Array实例的类型判断

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

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

     var class2type={
"[object Array]":"array",
"[object Boolean]":"boolean",
"[object Date]":"date",
"[object Function]":"function",
"[object Number]":"number",
"[object Object]":"object",
"[object RegExp]":"regexp",
"[object String]":"string"
};

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

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

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

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

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

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

对于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对象,最后实现如下:

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

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

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

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style rel="stylesheet" type="text/css">
</style>
<script type="text/javascript">
var class2type={
"[object Array]":"array",
"[object Boolean]":"boolean",
"[object Date]":"date",
"[object Function]":"function",
"[object Number]":"number",
"[object Object]":"object",
"[object RegExp]":"regexp",
"[object String]":"string"
};
function type(obj){
return obj == null ? String(obj) : class2type[Object.prototype.toString.call(obj)] || "object";
}
function isFunction(obj){
// 这样判断解决了FF下对embed、object、applet的typeof返回"function"的陷阱.
return type(obj) === "function";
}
function isArray(obj){
// 解决了instanceof Array不能对iframe中的Array对象正确判断的陷阱.
return type(obj) === "array";
}
function isNaN(obj){
return obj !== obj;
}
function isNull(obj){
return obj === null;
}
function isNull2(obj){
return type(obj) === "null";
}
function isUndefined(obj){
return obj === void 0;
}
function isUndefined2(obj){
return type(obj) === "undefined";
}
// 只要可以转换为运算数字.
function isNumberic(obj){
return !isNaN(parseFloat(obj)) && isFinite(obj);
}
// Number.NEGATIVE_INFINITY-Number.NEGATIVE_INFINITY为NaN,经过判断后返回false.
function isNumberic2(obj){
return obj - parseFloat(obj) >= 0;
}
// 通过Object.prototype.toString判断,但IE678下通过HACK判断.
function isWindow(obj){
return obj != null && (/Window|global/.test(Object.prototype.toString.call(obj))||(obj==obj.document&&obj.document!=obj));
}
function isPlainObject(obj){
if(!obj||type(obj)!=="object"||obj.nodeType||isWindow(obj)){
return;
}
try{
// 存在构造函数但最近的原型对象中不存在isPrototypeOf这个Object原型特有属性。
if(obj.constructor&&!Object.prototype.hasOwnProperty.call(obj.constructor.prototype,"isPrototypeOf")){
return false;
}
}
catch(e){
// IE678都有可能在这里抛出异常,当obj为一些没有暴露constructor的原生对象的时候.
return false;
}
var key;
for(key in obj){}
// for in 循环先枚举非继承属性,再枚举继承属性.
// 如果对象的最后一个属性是非继承属性,那么所有属性都是非继承属性.
return key === undefined || Object.prototype.call(obj,key);
}
window.onload=function(){
/* 进行一些测试 */
var br="<br/>"
var nframe=document.body.appendChild(document.createElement("iframe"));
var frame=window.frames[window.frames.length-1];
nframe.style.display="none";
// IE678无法获取
if("Array" in frame){
var fArray = frame.Array;
var a=new fArray(1);
document.body.innerHTML += "instanceof判断Array在跨框架下的结果:" + (a instanceof Array) + br; // false.
document.body.innerHTML += "通过原型的toString方法判断的结果:" + isArray(a) + br; // true.
}
document.body.innerHTML += "window:" + isWindow(window) + br; // true.
document.body.innerHTML += "new Object:" + isPlainObject({}) + br;
}
</script>
</head>
<body><div class="show">HELLO</div></body>
</html>

JavaScript 类型判断 —— typeof 以及 instanceof 中的陷阱的更多相关文章

  1. JavaScript类型判断instanceof与typeof对比

    经常有人会在JavaScript里写如下的方法: function checkType() { var s1 = 123; var s2 = "OK"; if (s1 instan ...

  2. JS类型判断typeof PK {}.toString.call(obj)

    参考链接:https://www.talkingcoder.com/article/6333557442705696719 先看typeof <!doctype html> <htm ...

  3. JavaScript类型判断

    几种方法:typeof,instanceof,Object.prototype.toString,constructor,duck type ES6引入了一种新的原始数据类型Symbol,表示独一无二 ...

  4. JavaScript类型判断详解(Object.prototype.toString.call()方法进行数据类型的可靠判断)

    前言 在编写一些类库中,我们经常需要判断一些未知的用户的输入和配置,故而需要进行一系列的类型判断.故而总结下JS是如何进行类型判断的 typeof typeof操作符返回一个字符串,表示未经计算的操作 ...

  5. JS基础-数据类型判断typeof、instanceof、Object.prototype.toString

    typeof用在基本数据类型和函数时,返回其对应类型的描述,对于引用类型都返回为object. instanceof无法判断基本数据类型,对于引用类型数据,返回其其对应类型. Object.proto ...

  6. javascript类型判断方法

    判断javascript中的类型,共有四种常用的方法 var a=6; var b="str"; var c=true; var arr=[]; typeof 用于基本类型的判断 ...

  7. javascript类型判断最佳实践

    javascript有8种数据类型 值类型 Number Null Undefined String Symbol Boolean BigInt 引用类型 Object Array Function ...

  8. javaScript 类型判断

    直接上例子: 1 判断是否为数组类型 2 判断是否为字符串类型 3 判断是否为数值类型 4 判断是否为日期类型 5 判断是否为函数 6 判断是否为对象 1 判断是否为数组类型 linenum < ...

  9. JavaScript 类型判断的那些事

    先准备几个变量 var a = "abcde."; var b = 222; var c= [1,2,3]; // 或者 new Array() var d = new Date( ...

随机推荐

  1. hadoop_并行写操作思路

    这篇文章是关于,如何修改hadoop的src以实现在client端上传大文件到HDFS的时候, 为了提高上传的效率实现将文件划分成多个块,将块并行的写入到datanode的各个block中 的初步的想 ...

  2. iOS---There was an internal API error 错误

    There was an internal API error. 错误原因:把Product Name作为程序名称,程序名称错乱 解决方法:检查Product Name, 不要包含中文以及特殊字符.在 ...

  3. CSS 组合选择符

    CSS 组合选择符 组合选择符说明了两个选择器直接的关系. CSS组合选择符包括各种简单选择符的组合方式. 在 CSS3 中包含了四种组合方式: 后代选取器(以空格分隔) 子元素选择器(以大于号分隔) ...

  4. php redis 【摘自网上,只为参考】

    ubuntu下安装php redis sudo apt-get install redis-server 测试redis是否安装成功:注意:要开启redis redis-cliset test hel ...

  5. Java RMI 框架_远程方法调用(2016-08-16)

    概念: Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可 ...

  6. 朋友的礼物(英雄会,csdn,高校俱乐部)信封问题,匹配模型

    前言: 首先这是一题解,但是重点最代码之后,有耐心的可以直接从代码后看. 上题目:n个人,每个人都有一件礼物想送给他人,他们决定把礼物混在一起,然后每个人随机拿走一件,问恰好有m个人拿到的礼物恰好是自 ...

  7. ASP.Net定时任务执行

    原料: System.Timers.Timer():通过.NET  Thread  Pool实现的,轻量,计时精确,对应用程序.消息没有特别的要求:缺点是不支持直接的拖放,需要手工编码. Timer的 ...

  8. HTML5 程序设计笔记(二)

    Canvas API 1.HTML5 Canvas 概述 1.1 历史 Canvas的概念最初是由苹果公司提出的,用于在Mac OS X WebKit中创建控制板部件(dashboard widget ...

  9. JS中关于clientWidth offsetWidth srollWidth等的含义

    网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...

  10. 使用jQuery验证用户名是否存在,达到局部刷新的效果

    <%@ page pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...