JavaScript类型判断精髓:深入理解与实战技巧

397 阅读4分钟

前言

js有哪些数据类型?

原始类型和引用类型

原始类型:String、Number、Boolean、Undefined、Null、BigInt、Symbol(BigInt、Symbol在上篇文章有介绍)

引用类型:对象、函数、数组、Date(日期对象,返回当前时间)

判断类型有哪几种方法呢?

有四种

  • typeof
  • instanceof
  • object.prototype.toString.call()
  • Array.isArray()

typeof

  • 可以判断除了null之外的所有原始类型
  • 除了function其他所有引用类型都会被判断成object
  • typeof通过将值转换为二进制后判断其二进制前三位是否为0,是则为object(对象转换为二进制后的前三位为0,null特殊,转换为二进制全是0,所以会被判成object)
let s = '123'
let n = 123
let b = true
let u = undefined
let nul = null
let sy = Symbol(123)
let big = 999999999999999999999999999999999999999n


let obj = {}
let arr = []
let fn = function () {}
let date = new Date() // 日期对象

console.log(typeof s);
console.log(typeof(n));
console.log(typeof nul, typeof b, typeof u, typeof nul, typeof sy, typeof big);
console.log(typeof obj, typeof arr, typeof fn, typeof date);

打印结果 image.png typeof有两种写法,带()和不带()

console.log(typeof s);
console.log(typeof(n));

instanceof

  • 只能判断引用类型
  • 通过原型链查找来判断类型 (不了解可以去看看我写的原型和原型链那篇文章)
let s = '123'
let n = 123
let b = true
let u = undefined
let nul = null
let sy = Symbol(123)
let big = 999999999999999999999999999999999999999n


let obj = {}
let arr = []
let fn = function () {}
let date = new Date() // 日期对象

console.log(obj instanceof Object);
console.log(arr instanceof Array);
console.log(fn instanceof Function);
console.log(date instanceof Date);
console.log(s instanceof String);
console.log(arr instanceof Object);

打印结果

image.png

分析

instanceof判断类型返回的是布尔值,左边的类型是否是右边,是则返回true,否则返回false。

教你手写一个与instanceof功能相同的方法myinstanceof

function myinstanceof(left, right) {
    while(left !== null) {
        if(left.__proto__ == right.prototype) {
            return true
        }
        left = left.__proto__
    }
    return false
}
console.log(myinstanceof([], Array)); // true
console.log(myinstanceof([], Object)); // true
console.log(myinstanceof({}, Array)); // false

打印结果

image.png

分析

首先你要了解arr.__proto__ = Array.prototype;Array.prototype.proto = Object.prototype;一直往上找原型找不到了就是null返回false,找得到就把left.__proto__赋给left。

object.prototype.toString.call

object.prototype.toString(this)只能判断对象的类型,且判断有五个步骤

  • 如果toString()接收到的值是undefined,抛出错误
  • 如果toString()接收到的值是null,抛出错误
  • 将 O 作为 ToObject(this) 的执行结果
  • 定义一个class作为内部属性 [[class]] 的值
  • 返回由 "[object " 和 class 和 "]" 组成的字符串
var a = {}
var b = new Date();
var c = function(){};
var d = [];
var e = 123;
console.log(Object.prototype.toString(a));
console.log(Object.prototype.toString(b));
console.log(Object.prototype.toString(c));
console.log(Object.prototype.toString(d));
console.log(Object.prototype.toString(e));

打印结果

image.png

分析 console.log(Object.prototype.toString(e));打印结果为什么不是[object Number]而是[object object],因为toString在接收到一个number类型时会执行ToObject。ToObject会new Number创建一个实例对象,返回的是一个对象。所以会打印出[object object]。所以这个方法不能准确地判断类型。

ToObject的执行规则: image.png

image.png

object.prototype.toString.call能判断所有类型

利用了Object.prototype.toString方法,并通过.call()方法改变其内部的this绑定,使其能够应用于任何对象,从而判断该对象的具体类型

var a = {}
var b = new Date();
var c = function(){};
var d = [];
var e = 123;
console.log(Object.prototype.toString.call(a));
console.log(Object.prototype.toString.call(b));
console.log(Object.prototype.toString.call(c));
console.log(Object.prototype.toString.call(d));
console.log(Object.prototype.toString.call(e));

执行结果

image.png

教你手写一个mycall方法实现和call一样的效果(深入了解call的原理)

Function.prototype.mycall = function(context) {
    // 调用的哥们是不是函数体
    if (typeof this !== 'function' ) {
        return new TypeError(this + 'is not a function');
    }

    // this里面的this => context
    const fn = Symbol('key');
    context[fn] = this;// 让对象拥有该函数 {Symbol('key'): foo}
    context[fn]();// 触发了隐式绑定
    delete context[fn];
}

var obj = {
    a:1
}
function foo() {
    console.log(this.a);
}


foo.mycall(obj);

打印结果

image.png

分析

因为mycall方法是实现类似于call方法,即调用该方法的函数foo的this指到mycall接收到的对象obj上面去。其实mycall是通过让obj增加一个属性,其属性的值为this属性名为Symbol创建的一个独一无二的键名,这个this就是调用mycall方法的函数,为了让obj对象拥有该函数foo,然后再让对象调用该函数触发隐式绑定规则,这样调用mycall方法的函数foo的this就指向了obj,最后再清除obj内的foo函数。

Array.isArray

只用于判断某个变量是否为数组类型的一个内置函数

let a = [];
let b = {};
let c = 123;
console.log(Array.isArray(a));
console.log(Array.isArray(b));
console.log(Array.isArray(c));

执行结果

image.png

结语

类型判断是JavaScript编程中的基础而又重要的环节,灵活运用各种方法,并根据具体需求和环境选择最合适的做法,将有助于编写更加健壮和高效的代码。随着语言的演进,持续学习新的特性和最佳实践也是每个开发者不可或缺的部分。

qiuzan.jpg