注册
web

一文搞懂JS类型判断的四种方法

前言


在JavaScript中,类型判断是一个非常基础但也十分重要的知识点。不同的类型判断方法适用于不同的场景,掌握这些方法可以帮助我们更好地理解和使用JavaScript。本文将详细介绍typeofinstanceofObject.prototype.toString以及Array.isArray这四种常用的类型判断方法,并通过实例代码帮助大家加深理解。


正文


typeof


typeof操作符可以用来判断基本数据类型,如stringnumberbooleanundefinedsymbolbigint等。它对于null和所有引用类型的判断会返回"object",而对于函数则会返回"function"


特点:



  1. 可以判断除null之外的所有原始类型。
  2. 除了function,其他所有的引用类型都会被判断成object
  3. 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属性是否出现在某个实例对象的原型链上。因此,它主要用于判断引用类型


特点:



  1. 只能判断引用类型。
  2. 通过原型链查找来判断类型。

示例代码:


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,所有还要改进一下:


我们要知道这么一件事情:



  1. 内置构造函数的原型链

    • 大多数内置构造函数(如 ArrayFunctionDateRegExpErrorNumberStringBooleanMapSetWeakMapWeakSet 等)的原型(Constructor.prototype)都会直接或间接地继承自 Object.prototype
    • 这意味着这些构造函数创建的对象的原型链最终会指向 Object.prototype


  2. 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.toStringFunction.prototype.call 两个方法的功能。


特点:



  1. 可以判断任何类型

代码示例


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 方法的执行步骤如下:



  1. 如果此值未定义,则返回 "[object undefined]"
  2. 如果此值为 null,则返回 "[object Null]"
  3. 定义 O 是调用 ToObject (该方法作用是把 O 转换为对象) 的结果,将 this 值作为参数传递
  4. 定义 class 是 O 的 [[Class]] 内部属性的值
  5. 返回 "[object" 和 class 和 "]" 组成的字符串的结果

关键点解释



  • ToObject 方法:将传入的值转换为对象。对于原始类型(如 stringnumberboolean),会创建对应的包装对象(如 StringNumberBoolean)。对于 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中的类型判断方法,感谢你的阅读!


image.png


作者:反应热
来源:juejin.cn/post/7416657615369388084

0 个评论

要回复文章请先登录注册