原生JS | 数据类型检测,并没你想象的那么简单

HTML5学堂-码匠:看上去,JavaScript中的数据类型检测,并没有什么难度,但是……它包含了不少的知识,如果你只知道一个typeof的话,那很建议你读读这篇文章,加强一下~

最近一个关系很不错的朋友在跳槽,面试一家大型知名互联网公司的时候,面试官问了一个看上去“超级”基础的问题:如何进行数据类型的检测啊?

面试官:能告诉我如何进行数据类型的检测吗?

码匠好友:typeof啊~

面试官:还有吗?

码匠好友:instanceof

面试官:还有别的吗?你了解几种?

码匠好友:jQuery的isArray、isFunction等函数

面试官:能说一下isArray这类函数的实现原理吗?

码匠好友:额,有些记不清了,与对象的原型相关。

面试官:你平时比较常用哪种?

码匠好友:敏捷开发的时候通常使用jQuery的函数,原生的话,会使用typeof和instanceof结合实现。

面试官:typeof不足之处在于什么?

码匠好友:无法检测“对象型数据的具体分类”

………………后略………………

数据类型检测方法

1 最为基础的typeof

2 不可不知的instanceof

3 比instanceof更好的constructor

4 检测值或表达式结果是否为NaN

5 易用的jQuery函数-isFunction、isArray等

6 高大上的原型方法 Object.prototype.toString

typeof 基本数据类型的检测

语法与范例

<script>
    // 语法:typeof 被检测的内容;
    var num = 2;
    console.log(typeof num);
</script>

基本解析

typeof是一个运算符,针对一个操作数(操作数可以是变量也可以是常量)进行运算,其返回值是一个字符串,返回值包括:"number"、"string"、"boolean"、 "undefined"、"object"、"function"。

<script>
    var user;
    var obj = {
        user : 'HTML5学堂'
    }
    var h5course = function(){
        console.log('码匠');
    }
    console.log(typeof 123);
    console.log(typeof 'html5');
    console.log(typeof true);
    console.log(typeof user);
    console.log(typeof obj);
    console.log(typeof h5course);
</script>

typeof的局限性

typeof的问题在于:针对对象类型数据,无法进行具体细化的检测。对于数组、正则、对象{}、null等数据,虽然均属于对象类型,但却各不相同,使用typeof进行检测时,均返回object。

<script>
 console.log(typeof null);
    console.log(typeof {'user': 'HTML5学堂'});
    console.log(typeof [4, 20]);
    console.log(typeof (/\d{8}/));
</script>

不可不知的instanceof

面对typeof的类型检测缺陷,可以使用instanceof来弥补。

语法与范例

<script>
 // 语法: 要检测的内容 instanceof 类型
    console.log([4,20] instanceof Array);
    console.log(/\d{8}/ instanceof RegExp);
    // 如上两个返回值均为true
</script>

基本解析

instanceof,能够用于数据类型的检测,但是仅限于引用类型数据,无法检测基本数据类型的值;检测的返回值内容是布尔值。此外,会受到原型链的影响。

注:关于值类型与引用类型变量,如果不明白可以戳文章底部的相关文章链接进行查看。

instanceof的局限性

局限性1:不能够检测以“字面量”方式出现的基本数据类型;

<script>
 // 局限1 - 字面量式的基本数据类型无法检测
    var str = 'HTML5学堂';
    var str2 = new String('码匠');
    console.log(str instanceof String);
    // str的检测返回false
    console.log(str2 instanceof String);
    // str2的检测返回true
</script>

代码解析:str是使用“字面量”的方式创建的字符串,而str2是使用String对象实例化的字符串。检测str时,返回结果为false;而检测str2时,返回结果为true。

局限性2:会检测该类所归属的原型链,只要在原型链当中能够找到,检测结果均为true,检测结果有可能会出现问题。

<script>
 // 范例1
    var str = new String('码匠');
    console.log(str instanceof String);
    console.log(str instanceof Object);
    // 范例2
    var box = document.getElementsByTagName('body');
    console.log(box[0] instanceof HTMLBodyElement);
    console.log(box[0] instanceof Node);
    console.log(box[0] instanceof Object);
    // 范例3
    var arr = ['HTML5学堂', '码匠'];
    console.log(arr instanceof Array);
    console.log(arr instanceof Object);
</script>

代码解析:

在此处如果不是很了解原型链的童鞋,可以换个角度来理解。例如:数组(Array)隶属于对象(Object),对于这种归属,检测也是成功的。

还不理解?好,我们换一个更易懂的!人类,属于哺乳动物,此时检测我们是否是哺乳动物,结果会是什么呢?

范例1中的字符串,的确属于string类型,但是它是通过String构造函数实例化得到的,String本身是一个字符串对象,所以str也符合Object这个条件。

范例2中的body标签本身是一个对象,细化一些说是一个“节点对象”,再细化说,是一个“body元素”,所以三种检测均为true。范例3中的数组同理(数组属于对象的一个分类)

比instanceof更好的constructor

语法与范例

<script>
 // 语法: 要检测的内容.constructor === 类型
    console.log([4, 20].constructor === Array);
    console.log(/\d{8}/.constructor === RegExp);
    console.log('码匠'.constructor === String);
    var num = 123;
    console.log(num.constructor === Number);
    // 如上几个返回值均为true
</script>

基本解析

constructor是对象的一个属性,不是运算符,constructor属性指向对象的构造函数。constructor的作用与instanceof基本类似,但是它对instanceof的两个缺陷均进行了弥补,也就是说:既能够检测基本数据类型,又不受到原型链的影响。

<script>
 // 范例1
    var box = document.getElementsByTagName('body');
    console.log(box[0].constructor === HTMLBodyElement);
    console.log(box[0].constructor === Node);
    console.log(box[0].constructor === Object);
    // 范例1中只有第一个返回true,后两者均返回false
    // 范例2
    console.log([4, 20].constructor === Array);
    console.log([4, 20].constructor === Object);
    // 范例2中,Array的返回true,而Object的检测返回false
</script>

constructor的局限性

对于自己创建的构造函数,constructor的局限性会比较大(当然这里不是我们主要要讨论的东西),constructor属性是易变的,可以进行定义,所以并不能够保证它指向相应的构造函数。但是,对于系统的各类构造函数,还是可以正常使用的,毕竟我们平日里并不会去修改系统默认对象的constructor指向的。

<script>
 function Me() {};
    var demo = new Me();
    console.log(demo.constructor); 
    // 此处检测的结果是,constructor指向构造函数Me,但是我们却可以人工修改指向,比如修改为Peo(如下代码)
    function Peo() {};
    demo.constructor = Peo;
    console.log(demo.constructor);
    // 此处检测的结果就变成了Peo
</script>

相关说明

如果希望了解instanceof、constructor的基本原理,需要掌握原型,了解构造函数的内在机制。如果在这方面积累不太够的小伙伴,建议可以先掌握这些知识点,然后后期随着自己知识的深入逐渐的理解实现原理。

检测值或表达式结果是否为NaN

isNaN函数

isNaN用于检测值或表达式“转换为数字”时,是否为NaN。可以用于辅助parseFloat()和parseInt()进行进一步的结果检测。

<script>
 console.log(isNaN(NaN));
    console.log(isNaN(123+234));
    console.log(isNaN('a1234'));
</script>

易用的JQ函数-isArray等

jQuery当中,提供了大量的数据类型检测方法(isArray、isFunction等等),可以检查数据属于哪种具体的对象类型,此处就不多谈了,感兴趣的查看JQ的API文档即可。

Object.prototype.toString.call()

语法与范例

<script>
 console.log(Object.prototype.toString.call(123));
    console.log(Object.prototype.toString.call(null));
    console.log(Object.prototype.toString.call(true));
    console.log(Object.prototype.toString.call('码'));
    console.log(Object.prototype.toString.call(/\d/));
    console.log(Object.prototype.toString.call([4]));
    // 如上的打印结果:
    // [object Number]
    // [object Null]
    // [object Boolean]
    // [object String]
    // [object RegExp]
    // [object Array]
</script>

基本解析

Object.prototype.toString比较常用于判断对象值属于哪种内置属性,返回值类型为字符串,返回的字符串格式为:"[object 数据类型]"。由于许多引用类型都重写了Object继承来的toString方法,所以通常使用call/apply方法,借用Object.prototype.toString函数来判断数据类型。

每一种数据类型所属的类的原型上都有toString方法,例如:Number.prototype、String.prototype、Array.prototype等等。除了Object上的toString之外,其他类原型上的toString都用于将数据值转换为字符串。

Plus

可以借助字符串截取的方法,获取Object.prototype.toString的结果,并进行处理,从而得到“Number”、“Null”等数据类型字符串,从而更方便进行数据类型比较/检测。

<script>
 var demo = '测试用data';
    var type = Object.prototype.toString.call(demo).slice(8, -1);
    console.log(type);
</script>

原文发布于微信公众号 - HTML5学堂(h5course-com)

原文发表时间:2017-05-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Coding迪斯尼

使用普拉特解析法解析复杂的算术表达式

19020
来自专栏从流域到海域

《笨办法学Python》 第29课手记

《笨办法学Python》 第29课手记 本节课讲if语句。 本节内容比较简单,如果觉得你的代码没有错误,但运行时报错,那么你的代码肯定有错误。相信我解释器是已经...

21060
来自专栏Java开发者杂谈

Thinking in java(1):对象导论

纯粹的面向对象程序设计的几个特性: 1. 万物皆对象 2. 程序是对象的合集,他通过发消息告诉彼此要做什么 3. 每个对象都有自己的由其他对象所构成的存储 4....

383120
来自专栏Python入门

十年Python大牛花了三天总结出来的python基础知识实例,超详细!

6、变量在内存中是通过引用计数来跟踪管理的~想要一起学习Python的可以加裙227-435-450,裙内有各种资料满足大家,欢迎加裙

36810
来自专栏数据小魔方

左手用R右手Python系列13——字符串处理与正则表达式

学习数据分析,掌握一些灵巧的分析工具可以使得数据清洗效率事半功倍,比如在处理非结构化的文本数据时,如果能够了解一下简单的正则表达式,那么你可以免去大量的冗余代码...

39540
来自专栏程序员叨叨叨

6.8 控制流语句(Control Flow Statement)

程序最小的独立单元是语句(statement),语句一般由分号结尾,缺省情况下,语句是顺序执行的,但是当涉及逻辑判断控制时,就要求有控制流程序语句。控制流程序语...

28630
来自专栏编程

浅谈如何定义和调用Python的函数

函数是python编程核心内容之一,笔者在本文中主要介绍下函数的概念和基础函数相关知识点。函数是什么?有什么作用、定义函数的方法及如何调用函数。 函数是可以实现...

19550
来自专栏MyBlog

Effective.Java 读书笔记(8)关于equals方法

重写equals看上去十分简单对吧,但是我觉得很多时候重写equals可能会招致一些问题,这些问题有时可能会特别严重,当然了不重写不就完事了吗?但是这只适用于那...

9440
来自专栏Brian

Python 深浅拷贝

Python浅拷贝和深度拷贝 今天面试了一个计算机专业研究生且大学出身也很好,但是面试的结果来看并没有达到我的预期。很多基础计算机的知识貌似都不是很懂,更别说...

42980
来自专栏彭湖湾的编程世界

【算法】哈希表的诞生

参考资料 《算法(java)》                           — — Robert Sedgewick, Kevin Wayne 《数据结...

361100

扫码关注云+社区

领取腾讯云代金券