JS数据类型检测

前言

本章给大家带来 js 4 种数据类型检测和底层原理,你真的掌握数据类型检测了吗?

typeof 关键词

  • typeof检测null的结果是'object'
  • typeof无论什么检测数据类型最终都是返回字符串类型
  • typeof检测不存在的变量时不会报错,而是返回'undefined'
  • typeof检测对象除了function以外其他对象值都是'object'

举些例子

// 检测不存在的变量
console.log(typeof a); //=> 'undefined'

// 检测对象
console.log(typeof {}); //=> 'object'
console.log(typeof []); //=> 'object'

// 基于构造函数new出来的原始类型值也是'object'
console.log(new Number(1)); //=> 'object'
console.log(new String('string')); //=> 'object'

// 检测函数
function fn () {}
console.log(typeof fn) //=> 'function'
console.log(class A {}); //=> 'function'

// 检测原始值
console.log(typeof 1); //=> 'number'
console.log(typeof Number(1)); //=> 'number'
console.log(typeof NaN); //=> 'number'
console.log(typeof undefined); //=>'undefined'
console.log(typeof Symbol()); //=> 'symbol'
console.log(typeof 1n); //=> 'bigint'
//....

// typeof检测 null 的结果也是'object'
console.log(typeof null); //=> 'object' 
为什么typeof检测 null 也是'object'

所有数据类型值,在计算机底层都是以二进制的方式去存储 [64位]typeof在检测数据类型,是按照二进制的前三位值来判断的。这就导致了 null 和 对象值 前三位值一致,造成了错误的判断

二进制 表示类型的标签

  • 000:对象, 数据是对对象的引用。
  • 1:整数, 数据是一个 31 位有符号整数。
  • 010:浮点, 数据是对双浮点数的引用。
  • 100:字符串, 数据是对字符串的引用。
  • 110:布尔, 数据是一个布尔值。

有两个比较特殊

  • -2^30 undefined 整数范围之外的数字
  • null 是机器代码 NULL 指针。或者:一个对象类型标签加上一个为零的引用
// 这只是个比喻,可以看出前三位是一样,然后null就被认定为是object类型
'null' => 0000000...
'object' => 0001010... 

由于typeof 检测类型是通过二进制去判断,所以性能相对于其他的性能要好一些

检测funciton为什么不是'objcet'
  • function也是一个对象,为什么能被检测成'funciton'而不是'object'
  • 因为按照了 ECMA-262 规范实现 [[Call]],对象内部实现了[[Call]]就被认为是一个函数,返回'function'
typeof document.all === ‘undefined’

在我探索过程中还发现一个特别有趣的问题,就是 typeof 检测document.all时,结果是undefined

给大家留一个小作业,欢迎在评论区回答哦!

// 最后输出的结果是多少?
typeof typeof typeof 99; 

instanceof 关键词

  • 用于检测对象是否为该构造函数的实例
  • 非对象类型的检测会报错,所以确切的说并不是用来检测数据类型的
  • 原理是通过查找原型链 ( obj.__proto__ ) 来判断原型上是否具有这个属性
  • instanceof 存在 3 种缺陷
缺陷 1:

使用 Object 检测对象实例 都会为true

let arr = [];
console.log(arr instanceof Array); //=> true
console.log(arr instanceof Object); //=> true 
// 我们一般认为 ( arr instanceof Object ) 是 false, 但是它却为true
// 检测原理是 Object.prototype.isPrototype(arr) //=> true 

// 讲解:
// Object.prototype.isPrototype.(targetObj) 
// 用于检测一个对象原型是否存在目标对象上,只不过这里对象用的是 Object.prototype 
缺陷2:

一旦原型修改,检测结果将不准确

let Person = function Person () {};
Person.prototype = Array.prototype;
let p1 = new Person();
console.log(p1); 
console.log(p1 instanceof Array); //=> true

// 虽然p1可以基于__proto__找到Array.prototype
// 但是它不具备数组的任何特征(length/索引都没有的) 
缺陷3:

无法检测到原始(基本)数据类型

let num = 10;
let num20 = Number(20);
console.log(num instanceof Number); //=> false
console.log(num20 instanceof Number); //=> false

// 可以通过这种方式
let n = new Number(20); // 实例化
console.log(n instanceof Number); //=> true 
instanceof 底层原理
  • 在 ( 实例 instanceof 类 ) 的时候,会调用 类.prototype[Symbol.hasInstance](实例) 进行检测
  • instanceof 右边的值不是一个构造函数或类的时候则会报错
let obj = {};
let arr = [];
console.log(arr instanceof obj); 
// Uncaught TypeError: Right-hand side of 'instanceof' is not callable
// 右侧值无法调用 
instanceof 进行重写
function instance_of(obj, constructor) {// 参数校验if (obj === null || !(/^(object|function)$/i.test(typeof obj))) return false;if (typeof constructor !== "function") throw new error("Right-hand side of 'instanceof' is not callable"); 	//=> Object.getPrototypeOf 获取原型链的方法let proto = Object.getPrototypeOf(obj),prototype = constructor.prototype;//=> 一步步查找while (true) {if (proto === null) return false;if (proto === prototype) return true;proto = Object.getPrototypeOf(proto);}
}
console.log(instance_of(10, Number)); //=> false
console.log(instance_of(new Number(10), Number)); //=> true
console.log(instance_of(/^10$/, RegExp)); //=> true
console.log(instance_of([], {})); //=> Uncaught ReferenceError: error is not defined 

constructor 属性

  • 每个构造函数的原型上都会有一个 constructor 属性,指向的是它自身
  • 用于检测该构造函数是否和原型上的 constructor 属性一致
  • 缺陷是可以更改 constructor 属性,导致没有预期结果
let Person = function Proson() {};
let Animal = function Animal() {};
console.log(Person === Person.prototype.constructor); //=> true
Person.prototype.constructor = Animal;
console.log(Person === Person.prototype.constructor); //=> false 

Object.prototype.toString.call(val)

  • 检测数据类型最精确的方法
  • 唯一缺点就是看起来很长
let obj = {}.toString.call;
console.log(obj.call({})); //=> [object Object]
console.log(obj.call(window)); //=> [object Window]
console.log(obj.call(document.querySelectorAll("*"))); //=> [object NodeList] 

结尾

本章结束了,感谢您看到最后!如果对你有帮助请点一个赞,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值