判断js数据类型的四种方法和原理

1,586 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

怎么去判断一个数据属于哪个数据类型,这个是很常见的操作,我们一般都会想到typeofinstanceof这两个常见的方法,但有时候这两种方法并不能满足我们的需求。那让我们来看一下都有哪些可以判断数据类型的方法吧。

1. typeof

这个方法很常见,一般用来判断基本数据类型,如:stringnumberbooleansymbolbigintes10新增一种基本数据类型bigint,详细介绍),undefined等。 typeof 目前能返回stringnumberbooleansymbolbigintundefinedobjectfunction这八种判断类型 

   typeof '123'       string
   
   typeof 1           number
   
   typeof true        boolean
   
   typeof Symbol('1') symbol
   
   typeof 111n        bigint
   
   typeof undefined   undefined
   
   typeof null        object
   
   typeof {a:1,b:2}   object
   
   function c(){console.log('123')}
   
   typeof c          function
    

其实我一直有个疑问,null既然属于基本数据类型,为什么用typeof返回的是object呢?

解答如下:

js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息

000:对象

010:浮点数

100:字符串

110:布尔

1:整数

但是对于 undefinednull 来说,这两个值的信息存储是有点特殊的。

null:所有机器码均为0

undefined:用 −2^30 整数来表示

所以,typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待。

2.  instanceof

一般用来判断引用数据类型的判断,如:ObjectFunctionArrayDateRegExp

 /s/g instanceof RegExp    true
        
 new Date('2019/01/05') instanceof Date   true
        
 [1,2,3] instanceof Array   true
        

instanceof 主要的作用就是判断一个实例是否属于某种类型,例如:

let animal = function () {}
let monkey = new animal()
monkey instanceof animal    true

当然,instanceof 也可以判断一个实例是否是其父类型或者祖先类型的实例。

 let person = function () {}
 let programmer = function () {}
 programmer.prototype = new person()
 let nicole = new programmer()
 nicole instanceof person       true
 nicole instanceof programmer   true

但是 instanceof 的原理是什么呢?可以用下面的代码解释一下:

 function new_instance_of(leftVaule, rightVaule) { 
        let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
        leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
        while (true) {
            // 为什么有leftVaule === null这一层判断,因为null为原型链的终点,
            // 原型链最终都指向null,所有当leftVaule为null时,
            // 说明已经遍历完原型链上所有的原型对象,
            // 结果是:rightVaule不是leftVaule的构造函数
        	if (leftVaule === null) {
                return false;	
            }
            if (leftVaule === rightProto) {
                return true;	
            } 
            leftVaule = leftVaule.__proto__ 
        }
    }

其实 instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。 

3.  constructor

当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。如下所示:

当执行 var f = new F() 时,F被当成了构造函数,fF的实例对象,此时F原型上的constructor传递到了f上,因此f.constructor == F

可以看出,JS在函数F的原型上定义了constructor,当F被当作构造函数用来创建对象时,创建的新对象就被标记为了“F” 类型,使得新对象有名有姓,可以追溯。

同理,JS中的数据类型也遵守这个规则:

细节问题: 

1.nullundefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据可以通过第四种方法来判断。 

2.JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object 。

4.  Object.prototype.toString(这个是判断类型最准的方法)

toStringObject原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为**[object,xxx],xxx**是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,… 基本上所有对象的类型都可以通过这个方法获取到。

Object.prototype.toString.call('') ;         [object String]

Object.prototype.toString.call(1) ;          [object Number]

Object.prototype.toString.call(true) ;       [object Boolean]

Object.prototype.toString.call(undefined) ;  [object Undefined]

Object.prototype.toString.call(null) ;       [object Null]

Object.prototype.toString.call(new Function()) ;  [object Function]

Object.prototype.toString.call(new Date()) ; [object Date]

Object.prototype.toString.call([]) ;         [object Array]

Object.prototype.toString.call(new RegExp()) ; [object RegExp]

Object.prototype.toString.call(new Error()) ;  [object Error]

Object.prototype.toString.call(document) ;   [object HTMLDocument]

Object.prototype.toString.call(window) ;[object global] window是全局对象global的引用

需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object, 按照JS变量查找规则,其他对象应该也可以直接访问到ObjecttoString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致ObjecttoString被终止查找,因此要用call来强制执行ObjecttoString方法。 

这是我之前发表在其他平台上的原创文章。

人间正道是沧桑。

我们下篇文章再见!