前言
在现代JavaScript开发中,类型判断很重要。在这篇文章中,我们将探讨JavaScript中的类型判断,包括基本的typeof运算符、instanceof关键字,以及其他更现代的解决方案,让大家更加了解JS中在什么情况该使用哪种类型判断方法。
在聊类型判断之前我们先来回顾一下JS中有哪些数据类型
首先是基本类型
1.number 2.string 3.boolean 4.null 5.undefined 6.symbol 7.bigint
然后是引用类型
1.object 2.function 3.array 4.date
JS的类型判断方法
无论学习什么语言,总会有要让你判断数据类型的时候,今天我们就来介绍四种判断数据类型的方法
1.typepf
console.log(typeof 'hello');
console.log(typeof 123);
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof Symbol(1));
console.log(typeof 111n);
console.log(typeof null); // object
console.log('')
console.log(typeof {});
console.log(typeof []);
console.log(typeof new Date());
console.log(typeof function () { });
我们直接全部用typeof输出看一下
我们一个一个过,字符串hello输出string没毛病,number,boolean,undefined,symbol,bigint也能精准识别,但是一到null这里怎么就能输出了一个object嘞?这个问题我们先搁置在这里,继续是空对象{}识别的是object,看起来没有问题,但是到了空数组[]和 new date()怎么你也给我一个object呢?最后的函数判断没有问题。
面对以上两个问题,我们可以先来了解一下typeof的执行机制
typeof运行机制:typeof会将要判断的类型转换为二进制的形式然后对类型进行判断
而在这里我们就要知道一个老生常谈的bug,如果这个数据类型转换为二进制后前三位是000那么这个数据类型就是对象object(function除外),而null在转换后所有位数上都是0,因此也被判断成了object。
至此我们能知道
typeof 可以准确判断除了null之外的所有原始类型,不能判断引用类型(除了function)
2.instanceof
既然typeof无法判断引用类型,那么有什么办法能让我们判断引用类型吗?
instanceof说:“让我来!”
console.log({} instanceof Object);
console.log([] instanceof Array);
console.log(new Date() instanceof Date);
console.log(function(){} instanceof Function);
console.log('hello' instanceof String);
console.log(123 instanceof Number);
console.log(true instanceof Boolean);
instanceof精准的判断了所有引用类型,但是又无法判断基本类型
我们继续来看看这段代码
console.log(new Date() instanceof Object)
console.log([] instanceof Object)
console.log(function(){} instanceof Object);
我们发现居然是三个true,欸仔细一想之前学过的原型那一节好像也没有什么问题,当进行查找时,所有对象当中的隐式原型会不断向上查找,它们最终都会指向Object。那么根据这一特点这些引用类型是
Object类型好像也并不意外,接下来让我们了解一下instanceof的判断机制
instanceof通过原型链来判断类型相等,只能判断引用类型(原始类型没有隐式原型)
instanceof的核心逻辑是通过原型链检查对象是否是某个构造函数的实例。instanceof会检查object的原型链,验证是否存在Constructor.prototype。如果object的原型链上能找到Constructor.prototype,则返回true,否则返回false。
function Car() {
this.run = 'runing'
}
Bus.prototype = new Car();
function Bus() {
this.name = 'BYD'
}
let bus = new Bus();
console.log(bus instanceof Bus);
console.log(bus instanceof Car);
console.log(bus instanceof Object);
我们看到 bus 是 构造函数Bus new出来的实例对象,因此就会有bus的隐式原型就等于Bus的显示原型,即
bus._ _proto _ _ === Bus.prototype
Bus.prototype = new Car() 让Bus原型上的隐式原型等于Car的显示原型,即
Bus.prototype._ _proto _ _=== Car.prototype
bus._ _proto _ . _proto _ _ === Car.prototype
而Car.prototype上的隐式原型等于Object的显示原型
Car.prototype._ _proto _ _ ===Object.prototype
bus._ _ proto _ _ . _ _ proto _ _ ._ _ proto _ _ ._ _proto _ _ ===Object.prototype
我们前面说了instanceof不能判断基本数据类型,因为基本数据类型没有原型链,没有隐式原型
3.Object.prototype.toString.call()
那有没有方法是综合二者的优点,既能判断基本类型,还能判断引用类型的方法呢?答案是有的,Object的原型上就有一个方法。
let a = 1 // a = new number(1)
// a.toString = function(){}
// a.toString()
let b = {}
let c = null
console.log(Object.prototype.toString.call(a))
console.log(Object.prototype.toString.call(b))
console.log(Object.prototype.toString.call(c))
数字,对象,甚至连null都能给我们判断出来,接下来我来给大家介绍一下它的机制
- 如果this值为undefined,返回"[object Undefined]"
- 如果this值为null,返回"[object Null]"
- 设O为调用ToObject的结果,将this值作为参数传递ToObject(this),
- 设 class 为 O 的 [[Class]] 内部属性的值。 // 得到了 O 的类型
- 返回由“[object ”、class 和 “]” 三块拼接的结果
4.Array.isArray()
这是一个对专攻数组的方法,没错,它只能识别数组,其他的都会返回false
let arr = []
console.log(Array.isArray({}));
console.log(Array.isArray(arr));
拓展小知识
let num = 123 // let num = new Number(123)
num.a = 1
console.log(num.a); // undefined 为什么不报错?
console.log(num); // [[PrimitiveValue]]
其实v8引擎在读到这段代码时,它会进行这样一个操作。let num = 123会执行成let num = new Number(123)创建一个实例对象,然后读到num.a = 1这行代码时,它真的往实例对象上增加了属性a为1。
在读取值的时候V8引擎就会执行第一条定理:原始类型不能拥有属性和方法,属性和方法只能是引用类型。 所以v8引擎转念一想,不对!用户想要的是字面量,必须满足用户要求 就会执行 delete num.a。而当我们去访问对象上不存在的属性时,它不会报错,会输出undefined,所以上面那段代码不报错。
原始类型不能拥有属性和方法,属性和方法只能是引用类型的
访问对象上不存在的属性会得到 undefined