备战26春招,彻底搞懂前端八股之数据类型检测方法:知识点讲解+面试示例回答
知识点讲解
在JS中,有很多方法可以用来检测数据类型,它们各有优缺点。
typeof
先来看下 typeof 的用法:
typeof 123 // 'number'
typeof '123' // 'string'
typeof undefined // 'undefined'
typeof Symbol(1) //'symbol'
typeof true // 'boolean'
typeof {} // 'object'
typeof function(){} // 'function'
// ❗️❗️❗️注意
typeof null // 'object'
typeof [] // 'object'
由以上代码我们可以知道,typeof 返回的是一个表示类型的字符串,它可以检测大部分基础类型、函数 和 对象,但是检测不了 null 和 其他复杂数据类型。
那么问题来了:为什么 typeof null、typeof []会返回 'object' 而不是正确的类型呢?
在 JavaScript 最初的实现中,JavaScript 中的所有值是由二进制表示的,这个二进制的最后三位用来表示类型,比如0x12345000 后三位是000,表示它是个对象;0x12345001 后三位是001,表示它是个数字;等等等等。而 null 是个固定的值,它是 0x00000000,后三位恰好也是 0。
而 typeof 的底层原理,恰好就是检查最后三位,如果为 000,那么就返回 'object';如果是 001,那么就返回 'number',等等等等。所以说检查 null 时,由于它全部都是 0,最后三位当然也都是 0,所以就被错误地认为是 'object'.
typeof null 为 object,本质上是一个 bug,是个错误。曾有一个 ECMAScript 的修复提案想要解决这个 bug,遗憾的是,这个提案并没有被通过。这件事也提示我们,要对代码有敬畏心和责任心,写代码一定要谨慎、谨慎、再谨慎
instanceof
instanceof 我们已经在 原型链那一篇文章有过认识。准确来说,它是用来检测「构造函数的 <font style="color:rgb(0, 0, 0);background-color:rgb(237, 238, 240);">prototype</font> 属性是否出现在某个实例对象的原型链上」。它的用法如下:
function func() {
}
const a = new func();
const b = [];
a instanceof func; // true
a instanceof Object; // true
b instanceof func; // false
b instanceof Array; // true
b instanceof Object; // true
func instanceof Function; // true
func instanceof Object; // true
它的原理,就是检测右边值 的 <font style="color:rgb(0, 0, 0);background-color:rgb(237, 238, 240);">prototype</font> 属性是否出现在左边值的原型链上。
function myInstanceof(left, right) {
//注释掉的是一些特殊情况,我们不用管,看后续的核心逻辑就行
// // 右侧必须是函数(构造函数),否则会抛错
// if (typeof right !== 'function') {
// throw new TypeError('Right-hand side of instanceof is not callable');
// }
// // 原始值(number、string、boolean、symbol、bigint、null、undefined)直接返回 false
// if (left === null || (typeof left !== 'object' && typeof left !== 'function')) {
// return false;
// }
// 取右侧构造函数的原型对象
var prototype = right.prototype;
// 取左侧对象的原型链起点
var proto = Object.getPrototypeOf(left);
// 一直往上找原型链,直到尽头(null)
while (proto !== null) {
// 找到了同一个原型对象,返回 true
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
// 原型链到底也没找到
return false;
}
// 使用示例:
function Person(name) {
this.name = name;
}
var p = new Person('Tom');
console.log(myInstanceof(p, Person)); // true
console.log(myInstanceof(p, Object)); // true
console.log(myInstan
由以上内容我们可以知道,instanceof 只能用来判断复杂数据类型,无法判断基本数据类型。
Object.prototype.toString.call
在 Object 的显式原型对象上,有一个方法 toString。它的返回值是 <font style="color:rgb(0, 0, 0);background-color:rgb(237, 238, 240);">"[object Type]"</font>。如:
Object.prototype.toString.call(123) // '[object Number]'
Object.prototype.toString.call('23') // '[object String]'
Object.prototype.toString.call([1,2,3]) // '[object Array]'
Object.prototype.toString.call(()=>{}) // '[object Function]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(null) // '[object Null]'
这里要注意,不仅 Object.prototype 上面有 toString,Array.prototype 上面也有 toString,但是含义和作用完全不同。如果要判断类型,一定要使用 Object.prototype.toString
其他方法
Array.isArray 可以用来判断是否为数组
面试示例回答
问:在 JS 中,有哪些数据类型检测方法?有什么区别?
在 JS 中,常见的判断类型的方法有四种。分别为 typeof、instanceof、Object.prototype.toString.call 和 Array.isArray.
typeof 可以用来判断除了 null 以外的基本数据类型、函数、以及对象。它的返回值是一个小写开头的代表类型的字符串。它的缺点是,typeof null 是 object,这是因为 对象和 null 用二进制表示时,末尾三位表示类型的值都为 000,所以 typeof 会错误地把 null 判断为 object。typeof 适用于除了 null 以外的基本数据类型判断。
instanceof,本质上是在判断右侧值的 prototype 是否在左侧值的 原型链上。它适用于复杂数据类型的判断,尤其是我们创建了一个类或者构造函数,想判断一个对象是否为它的实例。
Object.prototype.toString.call 是最为全面的判断方法,它返回的是个字符串,字符串里是个中括号,中括号里左侧是 object,右侧是大写的数据类型,中间用空格隔开。这里要注意,其他的 prototype 上面也有 toString,当我们想要判断数据类型时,一定要准确地使用 Object.prototype.toString.call
还有一个可以判断是否为数组的方法,是 Array.isArray
在我们日常开发中,如果想要判断基本数据类型,除了 null 以外,我们可以用 typeof;如果想要判断是否为数组,可以用 Array.isArray,如果想要判断准确的数据类型,可以用 Object.prototype.toString.call;如果想要判断一个对象是否为某个构造函数的实例,可以用 instanceof。
内容来源: 前端新一水公众号
