「前端基础」深度分析数据类型检测四种方案的异同

引子

JavaScript 的数据类型检测是我们平时开发中经常会遇到的场景,小到基本数据类型大至各种引用数据类型的检测,都是我们需要掌握的知识点。本章会详细讲解 JavaScript 中各种不同数据类型的检测方法以及最后会实现一个数据类型检测的终极方法。

数据类型检测1:typeof

1)返回的结果都是字符串
2)字符串中包含了对应的数据类型
"number"/"string"/"boolean"/"undefined"/"symbol"/"object"/"function"
3)【局限性】
(1) typeof null => "object" null不是对象,它是空对象指针
(2) 检测数组或正则等特殊的对象,返回结果都是'object',所有无法基于typeof判断是数据还是正则





let obj = {};

此时obj存储的是堆的16进制地址,obj = AAAFFF000,因为堆内存不会被销毁 如果想让堆内存销毁,应让obj = null obj指向空对象指针 此时应用的是谷歌浏览器v8底层机制(在空闲时间随时监测没有被占用的内存,如果没有被占用,浏览器内部会手动帮我们销毁)

console.log(typ []);        // => "object"
console.log(typ typ []); // => "string"

数据类型检测2:instanceof

检测某个实例是否属于这个类
它检测的底层机制:所有出现在其原型链上的类,检测结果都是true
【局限性】
1、由于可以基于__proto__或者prototype改动原型链的动向
所以基于instanceof检测出来的结果并不一定是准确的
2、基本数据类型的值,不是对象类型,也没有__proto__,虽说也是所属类的实例,在JS中也可以调取所属类原型上的方法,但是instanceof是不认识的,故不能检测基本类型值




局限性
console.log(12 instanc Number); // => false
new Number(12) instanc Number; // => true

(12).__proto__; // => Number {0, constructor: ƒ, toExponential: ƒ, toFixed: ƒ, toPrecision: ƒ, …}
上面方法相当于浏览器帮我们实现了 new Number(12)
正确用法:
console.log(new Number(12) instanc Number); // => true
console.log([] instanc Array); //=>true
console.log([] instanc Object); //=>true
[] instanc Array
[] instanc Object
  dir()查看一个对象的所有属性
  dir([]) => 
   1、先在[]自身的__proto__原型链上查找,找到constructor:Array()
   2、紧接沿着原型链__proto__找到根级[].__proto__.__proto__(指向object.prototype)

只要在[]的原型链__proto__上检测结果都为true

但是这样导致一个问题,比如原型链指向被更改的问题,比如下面这种情况

Function Fn() {}
Fn.prototype.__proto__ = Array.prototype
let f = new Fn()

f的原型链 Fn -> Array() -> Object

弊端:如果手动更改原型链的指向,instanceof就不能准确判断出是哪种复杂数据类型了

arr.__proto__ = Object.prototype;
arr instanc Array; // => false

数据类型检测3:constructor

弊端:
1、对象的constructor属性可以随便改 2、对象的__proto__原型链一更改指向,导致constrctor也跟着改

let arr = [];
Array.prototype.constructor = null;
console.log(arr.constructor); // => null
arr.constructor === Array;  // => false

注:constructor和instanceof类似,都有不足

数据类型检测4:Object.prototype.toString.call([value]) / ({}).toString.call([value])

只有Object.prototype上的toString才能检测数据类型
不是用来转换为字符串的,而是返回当前实例所属类的信息
格式: "[object 所属类信息]"
例: "[object Object/Array/RegExp/Date/Function/Null/Undefined/Number/String/Boolean/Symbol...]"


很多内置类的原型上都有toString,接下来让我们一起看几个

比如:
Number/String/Boolean/Symbol它们的原型上都有:
toString(转化为字符串)
valueOf(返回它们的原始值)
Array/RegExp/Function等内置类的原型上都有:
toString(转化为字符串)
Object的原型上
toString(返回当前实例所属类的信息)
valueOf(返回原始值:基本类型值)







let arr = [12,23];
arr.toString();   // => "12,23"  此时调用的是Array.prototype.toString

同理
let fn = function() {};
fn.toString();   // => "function() {}"  调用的是Function.prototype.toString

但Object除外
let obj = {name:'AA'};
obj.toString(); // => "[object Object]"

让数组也调Object.prototype.toString
Object.prototype.toString.call([]);
类似 实现让类数组转化为真数组的方法,调用数组原型上的slice()
Array.prototype.slice.call(arguments); 
另一种写法 -> 很好的避免了typ和instanc的尴尬

({}).toString.call(12);                    // => "[object Number]"
({}).toString.call("12");                  // => "[object String]"
({}).toString.call(true);                  // => "[object Boolean]"
({}).toString.call(null);                  // => "[object Null]"
({}).toString.call(undefined);             // => "[object Undefined]"
({}).toString.call([]);                    // => "[object Array]"
({}).toString.call(function() {});         // => "[object Function]"

Object.prototype.toString.call()的优势

function Fn() {};
Fn.prototype = Array.prototype;
let f = new Fn();

Object.prototype.toString.call(f); // => "[object Object]"
f instanc Array;  // => true

[补]如何查看对象调用的是谁身上的toString()?
先在自身查找有无toString()方法,若没有,沿着__proto__原型链查找,直到找到为止

总结

  • 对于基本数据类型的检测采用 typeof
  • 对于引用数据类型(非严禁情况下)采用 instanceof,(严禁情况下)采用Object.prototype.toString.call()Object.prototype.toString.call() 这种方式基本没有什么局限性,是监测数据类型最准确的方式

写在最后

  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注
  • 在后续文章中,会通过剖析JQ库源码,研究其数据监测的方法
原文链接:juejin.im

上一篇:一个高性能的 Vue 高德地图组件库
下一篇:使用Pixi撸一款“劳力士绿水鬼”!

相关推荐

  • 重温Javascript:数据类型

    数据类型.png(https://img.javascriptcn.com/c8e734a064b405495051da1ed89158ac "数据类型.png") 类型判断 Javascri...

    8 个月前
  • 重学JS: 数据类型及类型检测

    数据类型 JavaScript七种内置类型: 空值(null) 未定义(undefined) 布尔值(boolean) 数字(number) 字符串(string)...

    1 年前
  • 转行学前端的第 44 天 : 了解 ECMAScript 数据类型转换(二)

    我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。 今日学习目标 昨天基于搜索基础学习了对象解构,然后今天就是准备学习一下之后在很多对象中提到的proto和prototype,...

    3 个月前
  • 转行学前端的第 43 天 : 了解 ECMAScript 数据类型转换(一)

    我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。 今日学习目标 昨天基于搜索基础学习了对象解构,然后今天就是准备学习之前一个小伙伴提到的拆箱和装箱,所以今天基础学习js类型转...

    3 个月前
  • 转行学前端的第 26 天 : 了解 ECMAScript 基础数据类型 (下篇)

    我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。 今日学习目标 昨天基于一些页面搜索,学习了《JavaScirpt 高级程序设计》(第三版)  第 3 章节中的 ES 基础概...

    3 个月前
  • 跨域的四种方式

    本文主要是关于跨域的几种方式,关于什么是跨域这里就不多说了,写这个也是为了记住一些知识点的。 一. jsonp jsonp的跨域方式很容易理解,页面的的每一个script标签浏览器都会发送get...

    1 年前
  • 谈谈JavaScript中的数据类型

    前言 迄今为止标准定义了 种数据类型: 种原始类型 、、 、 、 和 ; 种引用类型 看到这里,你是否已经对它们了如指掌呢。如果你还对它们之间的定义、转换、检测等方面并不是那么清楚,或者...

    1 年前
  • 读书笔记:数据类型、对象、原型、原型链、继承

    数据类型: 简单数据类型:Undefined、Null、String、Number、Boolean、Symbol 复杂数据类型:Object Undefined:声明,但未初始化 N...

    2 年前
  • 详解react关于事件绑定this的四种方式

    在react组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件,而且react还会对这种引用进行缓存,以达到cpu和内存的最大化。在使用了es6 class或者纯函数时,这种自...

    2 年前
  • 让你秒懂四种设计模式!

    0. 回顾过去 说实话,这标题有点儿 uc小编 的味道了。虽然真正的大佬已经对设计模式烂熟于胸,只希望我的学习记录能帮助到部分同学就足够了。经过我下面的介绍,你可以在极短的时间,了解并知道如何使用他...

    2 个月前

官方社区

扫码加入 JavaScript 社区