一文搞懂JS类型判断的四种方法
前言
在JavaScript中,类型判断是一个非常基础但也十分重要的知识点。不同的类型判断方法适用于不同的场景,掌握这些方法可以帮助我们更好地理解和使用JavaScript。本文将详细介绍typeof
、instanceof
、Object.prototype.toString
以及Array.isArray
这四种常用的类型判断方法,并通过实例代码帮助大家加深理解。
正文
typeof
typeof操作符可以用来判断基本数据类型,如string
、number
、boolean
、undefined
、symbol
、bigint
等。它对于null
和所有引用类型的判断会返回"object"
,而对于函数则会返回"function"
。
特点:
- 可以判断除
null
之外的所有原始类型。 - 除了
function
,其他所有的引用类型都会被判断成object
。 - typeof是通过将值转换为二进制后判断其二进制前三位是否为0,是则为object
示例代码:
let s = '123'; // string
let n = 123; // number
let f = true; // boolean
let u = undefined; // undefined
let nu = null; // null
let sy = Symbol(123); // Symbol
let big = 1234n; // BigInt
console.log(typeof s); // "string"
console.log(typeof n); // "number"
console.log(typeof f); // "boolean"
console.log(typeof u); // "undefined"
console.log(typeof sy); // "symbol"
console.log(typeof big); // "bigint"
console.log(typeof nu); // "object" - 特殊情况
let obj = {};
let arr = [];
let fn = function() {};
let date = new Date();
console.log(typeof obj); // "object"
console.log(typeof arr); // "object"
console.log(typeof date); // "object"
console.log(typeof fn); // "function"
function isObject(o) {
if (typeof o === 'object' && o !== null) {
return true;
}
return false;
}
let res = isObject({a: 1});
console.log(res); // true
instanceof
instanceof
用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上。因此,它主要用于判断引用类型
。
特点:
- 只能判断引用类型。
- 通过原型链查找来判断类型。
示例代码:
let obj = {};
let arr = [];
let fn = function() {};
let date = new Date();
console.log(obj instanceof Object); // true
console.log(arr instanceof Array); // true
console.log(fn instanceof Function); // true
console.log(date instanceof Date); // true
console.log(arr instanceof Object); // true
console.log(arr instanceof String); // false
console.log(n instanceof Number); // false
因为原始类型
没有原型而引用类型有原型,所有instanceof
主要用于判断引用类型
,那么根据这个我们是不是可以手写一个instanceof
。
手写·instanceof
实现:
首先我们要知道v8创建对象自变量
是这样的,拿let arr = []举例子:
function createArray() {
// 创建一个新的对象
let arr = new Array();
// 设置原型
arr.__proto__ = Array.prototype;
// 返回创建的数组对象
return arr;
}
V8 引擎会调用 Array
构造函数来创建一个新的数组对象,Array
构造函数的内部实现会创建一个新的空数组对象,并初始化其内部属性并且将新创建的数组对象的 __proto__
属性设置为 Array.prototype
,这意味着数组对象会继承 Array.prototype
上的所有方法和属性,最后,新创建的数组对象会被赋值给变量 arr
。
那么我们是不是可以通过实例对象的隐式原型
等于其构造函数的显式原型
来判断类型,代码如下:
function myInstanceOf(L,R){
if(L.__proto__ === R.prototype){
return true;
}
return false;
}
但是我们看到console.log([] instanceof Object); // true,所有还要改进一下:
我们要知道这么一件事情:
- 内置构造函数的原型链:
- 大多数内置构造函数(如
Array
、Function
、Date
、RegExp
、Error
、Number
、String
、Boolean
、Map
、Set
、WeakMap
、WeakSet
等)的原型(Constructor.prototype
)都会直接或间接地继承自Object.prototype
。 - 这意味着这些构造函数创建的对象的原型链最终会指向
Object.prototype
。
- Object.prototype 的原型:
Object.prototype
的隐式原型(即__proto__
)为null
。这是原型链的终点,表示没有更多的原型可以继承。
所以我们是不是可以这样:
function myinstanceof(L, R) {
while (L !== null) {
if (L.__proto__ === R.prototype) {
return true;
}
L = L.__proto__;
}
return false;
}
console.log(myinstanceof([], Array)); // true
console.log(myinstanceof([], Object)); // true
console.log(myinstanceof({}, Array)); // false
所以就完美实现了。
Object.prototype.toString.call
Object.prototype.toString.call
是一个非常有用的工具,可以用来获取任何 JavaScript 值的类型
信息。它结合了 Object.prototype.toString
和 Function.prototype.call
两个方法的功能。
特点:
- 可以判断任何类型
代码示例
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(123)); // [object Number]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call('hello')); // [object String]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(Symbol())); // [object Symbol]
console.log(Object.prototype.toString.call(123n)); // [object BigInt]
Object.prototype.toString
底层逻辑
根据官方文档,Object.prototype.toString
方法的执行步骤如下:
- 如果此值未定义,则返回
"[object undefined]"
。 - 如果此值为
null
,则返回"[object Null]"
。 - 定义
O
是调用ToObject
(该方法作用是把O
转换为对象) 的结果,将this
值作为参数传递。 - 定义
class
是O
的[[Class]]
内部属性的值。 - 返回
"[object"
和class
和"]"
组成的字符串的结果。
关键点解释
ToObject
方法:将传入的值转换为对象。对于原始类型(如string
、number
、boolean
),会创建对应的包装对象(如String
、Number
、Boolean
)。对于null
和undefined
,会有特殊处理。[[Class]]
内部属性:每个对象都有一个[[Class]]
内部属性,表示对象的类型。例如,数组的[[Class]]
值为"Array"
,对象的[[Class]]
值为"Object"
。
console.log(Object.prototype.toString(123));//[object Object]
console.log(Object.prototype.toString('123'));//[object Object]
console.log(Object.prototype.toString({}));//[object Object]
console.log(Object.prototype.toString([]));//[object Object]
为什么需要 call
?
Object.prototype.toString
方法默认的 this
值是 Object.prototype
本身。如果我们直接调用 Object.prototype.toString(123)
,this
值仍然是 Object.prototype
,而不是我们传入的值。因此,我们需要使用 call
方法来改变 this
值,使其指向我们传入的值。
手写call
obj = {
a:1,
}
function foo(){
console.log(this.a);
}
//我们需要将foo中的this指向obj里面
Function.prototype.myCall = function(context){
if(!(this instanceof Function)){ 在构造函数原型上,this指向的是实例对象,这里即foo
return new TypeError(this+'is not function')
}
const fn = Symbol('key'); //使用symbol作为key是因为可能会同名
context[fn] = this;//添加变量名为fn,值为上面的,context={Symbol('key'): foo}
context[fn](); // 触发了隐式绑定
delete context[fn]; //删除这个方法
}
foo.myCall(obj) // 1
console.log(obj); // {a:1}
我们知道call方法
是将函数里面的this强行掰弯到我们传入的对象里面去,它的原理是这样的,首先判断你传入的参数是不是一个函数,因为只有函数身上才有call方法,函数调用然后通过隐式绑定规则,将this指向这个对象,那么不就强行更改了this的指向,[不知道this的可以看这篇文章](你不知道的JavaScript(核心知识点概念详细整理-掘金 (juejin.cn))
Array.isArray
Array.isArray
是一个静态方法,用于检测给定的值是否为数组。
示例代码:
let arr = [];
let obj = {};
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
手写Array.isArray
实现:
function myIsArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
console.log(myIsArray(arr)); // true
console.log(myIsArray(obj)); // false
总结
typeof
适合用于检查基本数据类型,但对于null
和对象类型的判断不够准确。instanceof
用于检查对象的构造函数,适用于引用类型的判断。Object.prototype.toString
提供了一种更通用的方法来判断所有类型的值。Array.isArray
专门用于判断一个值是否为数组。
希望这篇文章能够帮助你更好地理解和使用JavaScript中的类型判断方法,感谢你的阅读!
来源:juejin.cn/post/7416657615369388084