zf-fe

✍️ Tangxt ⏳ 2020-06-15 🏷️ JS 专题

22-综合专题之检测数据类型的四种方案1

★概述

★第一种数据类型检测方式——typeof

面试的坑(也不算是坑):

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

运算规则是:从右往左!

★第二种和第三种数据类型检测方式——instanceof/constructor

◇instanceof

测试:

console.log(12 instanceof Number); // => false
console.log(new Number(12) instanceof Number); // => true
console.log([] instanceof Array); // => true
console.log([] instanceof Object); // => true

关于浏览器的隐式转换: (12).__proto__ <=> new Number(12).__proto__

可以看到 12 明明是Number的实例,但是 instanceof 就是不认

搞坏 instanceof 的测试:

let arr = []

function fn() {}
arr.__proto__ = fn.prototype
arr instanceof Array // -> false

instanceof

可以看到,通过JS,原型链基于 __proto__ 或者 prototype 是可以随意改动的,因此, instanceof 检测出来的结果并不一定准确! -> 所以 instanceof 只能用作于一个简单的参考姿势!

小结:

◇constructor

instanceof 基本一样

// 私有constructor属性找不到就找公有的
console.log([].constructor === Array) // -> true -> 表示 [] 为数组
console.log([].constructor === Object) // -> false -> 不可跨级
console.log(({}).constructor === Array) // -> false
console.log((12).constructor === Number) // -> true

constructor 这个值也是可以随便改的,而且比 instanceof 还要更恶心!如:

let arr = []
Array.prototype.constructor = null
arr.constructor === Array // -> false

凡是可以随便改的,都是不严谨的,或多或少会出现潜在的bug,不过一般来说,我们基本都不会去动 constructor 的指向,或者是原型链的指向……

总之,对于基本类型而言,我们也是可以用 constructor 来搞的! -> 简单参考用 constructorinstanceof 这两个东西吧!


就上边我们所看到的那样,不管是 typeofinstanceof ,还是 constructor ,都有自己很多的局限性

那么是否存在一种没有局限性的方案呢?

有的,那就是 Object.prototype.toString 方法啦! -> 注意,这是Object原型上的 toString 方法!可不是其它类(如Array等)上的 toString 方法!

★第四种数据类型检测方式——Object.prototype.toString.call

没有局限性的方案!

◇概述

注意,也是有 NullUndefined 这两个内置类的,只是我们开发者无法使用罢了 -> 你一使用就会报错!

敲黑板:

Object原型上的 toString 方法可不是用来转换为字符串的,而是返回当前实例所属类的信息

toString返回固定格式的值

◇使用

写法(本质上是其它类型数据的方法借用,就像是伪数组,如arguments借用数组的slice方法变为真正的数组):

Object.prototype.toString.call([value])

// or

({}).toString.call([value])

返回值的格式:

"[object 所属类信息]"

可能有的结果:

"[object Object/Array/RegExp/Date/Function/Null/Undefined/Number/String/Boolean/Symbol...]"

原理:

方法借用原理

数组字面量不需要这样 ([]) ,而普通对象是需要的 -> ({}) ,因为 {} 在JS中有太多含义了,如代码块,对象字面量等等,所以如果你这样 {}.toString() ,那么就会报语法错误!

测试(检测各种值的数据类型):

检测各种值的数据类型

即便你更改原型或原型链:

改原型

也不会影响 ({}).toString.call() 的判断!

所以:

这种方式基本上没有什么局限性,是检测数据类型最准确的方式

但在我看来,这还是有弊端的:

也是有弊端的toString方法


★总结

★Q&A

1)BigInt?

➹:JS最新基本数据类型:BigInt - 掘金