JavaScript如果正确的判断变量类型

233

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

前言

javascript是一门弱类型语言,并且制订初期比较粗糙,类型之间也存在很多隐式转换,很多时候我们不需要进行类型判断也能正确地实现我们的逻辑,但准确的判断类型在很多时候可以减少我们的代码错误,然后在类型判断中也存在很多坑。

这次把常用的变量(关键字)等进行实验,看看怎么判断类型检测最好:


// 测试的类型:
// string  number  boolean  object array  null  undefined
// regexp date function class window HTMLDomcument Set Symbol

const typeList = [
    'string',
    1,
    true,
    {},
    [],
    null,
    undefined,
    new RegExp('\s'),
    new Date(),
    function() {},
    class Preson {},
    window,
    document.getElementById('app'),
    new Set(),
    Symbol()
]

方法一:typeof 函数(关键字)

typeof是javascript提供的最简单的类型判断方法(也是最没用的),可以判断最简单的基础类型,对于对象类型无能为力。

typeof文档

类型检测结果为:


typeList = [
    'string', // string
    1, // number
    true, // boolean
    {}, // object
    [], // object
    null, // object
    undefined, // undefined
    new RegExp('\s'), // object
    new Date(), // object
    function() {}, // function
    class Preson {}, // function
    window, // object
    document.getElementById('app'), // object
    new Set(), // object
    Symbol() //symbol
]

typeList.forEach(el => {
    console.log(el,typeof el)
})

显然,typeof中很多类型都只能推断为object,只能用于一些基本类型的判断。

方法二:constructor

constructor是对象的构造函数,本着万事皆对象的原则,理论上一切变量都继承自对象类型(Object),而Object的原型链上有constructor属性,这个属性表示了创建该变量的构造函数的引用。

Object.prototype.constructor文档

类型检测结果为:


console.log('string'.constructor === String) // true

let num = 1
console.log(num.constructor === Number) // true

console.log(true.constructor === Boolean) // true
console.log({}.constructor === Object) // true
console.log([].constructor === Array) // true
console.log(null.constructor === null) // 报错:Uncaught TypeError: Cannot read properties of null (reading 'constructor')
console.log(undefined.constructor === undefined) // 报错:Uncaught TypeError: Cannot read properties of undefined (reading 'constructor')
console.log(new RegExp('\s').constructor === RegExp) // true
console.log(new Date().constructor === Date) // true
console.log((function() {}).constructor === Function) // true
console.log((class Preson {}).constructor === Function) // true
class Preson {}
console.log(new Preson().constructor === Preson) // true
console.log(window.constructor === Window) // true
console.log(document.getElementById('app').constructor === HTMLDivElement) // true
console.log(new Set().constructor === Set) // true
console.log(Symbol().constructor === Symbol) // true

因为是属性调用,所以nullundefined是无法使用该方法的,且constructor打印出来并不方便观察,所以一般只能用在判断是否等于某一种变量时使用,比如 [].constructor === Array

需要注意的是,constructor是可以被修改的。

方法三:instanceof

instanceofconstructor有些类似:

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

看定义有点绕,举例子说明会好理解些:[] instanceof Array,Array(构造函数)的prototype是否出现在[]的原型链上?

很显然,[]是个数组,它的原型链上一定能找到Array对象以及Array的prototype

instanceof文档

类型检测结果为:


console.log('string' instanceof String) // false
let num = 1
console.log(num instanceof Number) // false
console.log(true instanceof Boolean) // false
console.log({} instanceof Object) // true
console.log([] instanceof Array) // true
console.log(null instanceof null) // 报错
console.log(undefined instanceof undefined) // 报错
console.log(new RegExp('\s') instanceof RegExp) // true
console.log(new Date() instanceof Date) // true
console.log((function() {}) instanceof Function) // true
console.log((class Preson {}) instanceof Function) // true
class Preson {}
console.log(new Preson() instanceof Preson) // true
console.log(window instanceof Window)  // true
console.log(document.getElementById('app') instanceof HTMLDivElement)  // true
console.log(document.getElementById('app') instanceof HTMLElement) // true
console.log(new Set() instanceof Set)  // true
console.log(Symbol() instanceof Symbol)  // false

非引用类型无法通过instanceof来判断类型,同样nullundefined这两个特殊的类型使用instanceof也会报错。

还有两个比较特别的:

  1. Symbol作为一种特别的类型,打印s.prototypeundefined,所以也无法通过instanceof来判断类型。
  2. dom节点,HTMLDivElementHTMLElement都在原型链上,因为HTMLDivElementHTMLElement实际上也是继承关系,所以在原型链接上都存在,同样道理,万物皆对象,以下判断都为true

[] instanceof Object // true
{} instanceof Object // true

所以在判断一个变量是数组还是对象时,一定是判断 instanceof Array ,而不是 instanceof Object

方法四:Object.prototype.toString.call

万物皆对象,在这个例子中体现的淋漓尽致,使用这个方法基本上可以区分所有类型:

类型检测结果为:


const typeList = [
    'string', // [object String]
    1, // [object Number]
    true, // [object Boolean]
    {}, // [object Object]
    [], // [object Array]
    null, // [object Null]
    undefined, // [object Undefined]
    new RegExp('\s'), // [object RegExp]
    new Date(), // [object Date]
    function() {}, // [object Function]
    class Preson {}, // [object Function]
    window, // [object Window]
    document.getElementById('app'), // [object HTMLDivElement]
    new Set(), // [object Set]
    Symbol() // [object Symbol]
]
        
typeList.forEach(el => {
    console.log(el,Object.prototype.toString.call(el))
})

前面几种方法的问题几乎都不再存在,唯一的缺点就是打印结果有点难看,但是好在很标准,所以我们可以通过简单的函数封装,让类型判断更简单一些。

判断类型的方法封装:


function type(val) {
    const type = Object.prototype.toString.call(val)
    return type.slice(type.indexOf(" ") + 1,type.length - 1).toLocaleLowerCase()
}