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的情况,例如:
- <!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 中的陷阱的更多相关文章
- JavaScript类型判断instanceof与typeof对比
经常有人会在JavaScript里写如下的方法: function checkType() { var s1 = 123; var s2 = "OK"; if (s1 instan ...
- JS类型判断typeof PK {}.toString.call(obj)
参考链接:https://www.talkingcoder.com/article/6333557442705696719 先看typeof <!doctype html> <htm ...
- JavaScript类型判断
几种方法:typeof,instanceof,Object.prototype.toString,constructor,duck type ES6引入了一种新的原始数据类型Symbol,表示独一无二 ...
- JavaScript类型判断详解(Object.prototype.toString.call()方法进行数据类型的可靠判断)
前言 在编写一些类库中,我们经常需要判断一些未知的用户的输入和配置,故而需要进行一系列的类型判断.故而总结下JS是如何进行类型判断的 typeof typeof操作符返回一个字符串,表示未经计算的操作 ...
- JS基础-数据类型判断typeof、instanceof、Object.prototype.toString
typeof用在基本数据类型和函数时,返回其对应类型的描述,对于引用类型都返回为object. instanceof无法判断基本数据类型,对于引用类型数据,返回其其对应类型. Object.proto ...
- javascript类型判断方法
判断javascript中的类型,共有四种常用的方法 var a=6; var b="str"; var c=true; var arr=[]; typeof 用于基本类型的判断 ...
- javascript类型判断最佳实践
javascript有8种数据类型 值类型 Number Null Undefined String Symbol Boolean BigInt 引用类型 Object Array Function ...
- javaScript 类型判断
直接上例子: 1 判断是否为数组类型 2 判断是否为字符串类型 3 判断是否为数值类型 4 判断是否为日期类型 5 判断是否为函数 6 判断是否为对象 1 判断是否为数组类型 linenum < ...
- JavaScript 类型判断的那些事
先准备几个变量 var a = "abcde."; var b = 222; var c= [1,2,3]; // 或者 new Array() var d = new Date( ...
随机推荐
- Swift开源了,有什么好处?
昨天swift开源了,喜大泪奔的好消息! swift的官方网站https://swift.org swift在github的开源地址https://github.com/apple/swift 今天早 ...
- ERROR ITMS-90167: "No .app bundles found in the package"
http://stackoverflow.com/questions/37838487/error-itms-90167-no-app-bundles-found-in-the-package 简单说 ...
- C# 执行批处理文件(*.bat)的方法代码
代码如下: static void Main(string[] args){ Process proc = null; try { st ...
- 系统后台图表生成文档说明-javascript
1.引入jquery插件文件datas.js 2.各图表分类 表格 $('#'+tableId).mTable({ url:'', //数据来源,[必填] pageNum:1, //分页,默认为1,[ ...
- 数据库(学习整理)----5--Oracle常用的组函数
其他: 1.oracle中下标是从1开始的,Java下标是从0开始的 函数分类: 日期函数 字符函数 转换函数 数学函数 系统函数 ---在当前月份上面:增加.减少月份 select add_mont ...
- 【elasticsearch】(4)centos7 超简单安装elasticsearch 的 jdbc
前言 elasticsearch(下面简称ES)使用jdbc连接mysql比go-mysql-elasticsearch的elasticsearch-river-jdbc能够很好的支持增量数据更新的问 ...
- C#如何释放已经加载的图片 (转)
使用Image.FromFile取磁盘上的图片时,这个方法会锁定图片文件,而且会导致内存占用增大, 有几种方法解决: 一:将Image类转换成Bitmap类 System.Drawing.Image ...
- GoogleCode新手教程
GoogleCode页面介绍 Project Home 首先显示的是project home,页面左边的是这个项目的介绍,右边的License是说明使用的是什么开源协议,Labels是标签的意思,就是 ...
- 用vscode写博客和发布
最近想开始写点博客什么的,然后看到在博客园注册了一个账号这么久,也没有写过文章,就想在博客园写点什么来刷个存在感,而且觉得用Markdown编辑器来写文章挺不错,但是博客园自带的Markdown编辑器 ...
- LightOJ_1038 Race to 1 Again
题目链接 题意: 给一个数n, 每次操作是随机的选择一个[1,N]区间内能够被n整除的数进行除法, 然后得到一个新的n. 问n变成1时的期望操作次数. 思路: 设E[n] 为 当数为x时, 变成 1 ...