前言
本章给大家带来 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]
结尾
本章结束了,感谢您看到最后!如果对你有帮助请点一个赞,谢谢!