数据类型检测

55 阅读3分钟

数据类型检测

typeof:能检测除了null以外的所有基础类型和function,对于其他的引用类型都检测为object。

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'

思考:为什么null会被检测为object?

这是 JS 存在的一个悠久 Bug,不代表 null 就是引用数据类型,并且 null 本身也不是对象,其实这是因为null和object的底层标识符都是000,如果你需要在 if 语句中判断是否为 null,直接通过 ‘===null’来判断就好。

instanceof:可以正确判断对象的类型,其内部运行机制是判断在其原型链中是否找到该类型的原型。

let Car = function() {}
let benz = new Car()
benz instanceof Car // true
let car = new String('Mercedes Benz')
car instanceof String // true
let str = 'Covid-19'
str instanceof String // false

手写一个instanceof

function myInstanceof(left, right) {

  // 这里先用typeof来判断基础数据类型,如果是,直接返回false

  if(typeof left !== 'object' || left === null) return false;

  // getProtypeOf是Object对象自带的API,能够拿到参数的原型对象

  let proto = Object.getPrototypeOf(left);

  while(true) {                  //循环往下寻找,直到找到相同的原型对象

    if(proto === null) return false;

    if(proto === right.prototype) return true;//找到相同原型对象,返回true

    proto = Object.getPrototypeof(proto);

    }

}

// 验证一下自己实现的myInstanceof是否OK

console.log(myInstanceof(new Number(123), Number));    // true

console.log(myInstanceof(123, Number));                // false

Object.prototype.toString.call():Object.prototype.toString.call() 使用 Object 对象的原型方法 toString 来判断数据类型。

Object.prototype.toString({})       // "[object Object]"
Object.prototype.toString.call({})  // 同上结果,加上call也ok
Object.prototype.toString.call(1)    // "[object Number]"
Object.prototype.toString.call('1')  // "[object String]"
Object.prototype.toString.call(true)  // "[object Boolean]"
Object.prototype.toString.call(function(){})  // "[object Function]"
Object.prototype.toString.call(null)   //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g)    //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([])       //"[object Array]"
Object.prototype.toString.call(document)  //"[object HTMLDocument]"
Object.prototype.toString.call(window)   //"[object Window]"

constructor:constructor 有两个作用,一是判断数据的类型,二是对象实例通过constrcutor 对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了,这种不怎么用就不对比了知道就可以了。

思考:为什么obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样?

这是因为 toString 是 Object 的原型方法,而 Array、function等类型作为Object的实例,都重写了 toString 方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString() 不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。

手写通用类型检测

function getType(obj){

  let type  = typeof obj;

  if (type !== "object") {    // 先进行typeof判断,如果是基础数据类型,直接返回

    return type;

  }

  // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果

  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');  // 注意正则中间有个空格

}

/* 代码验证,需要注意大小写,哪些是typeof判断,哪些是toString判断?思考下 */

getType([])     // "Array" typeof []是object,因此toString返回

getType('123')  // "string" typeof 直接返回

getType(window) // "Window" toString返回

getType(null)   // "Null"首字母大写,typeof null是object,需toString来判断

getType(undefined)   // "undefined" typeof 直接返回

getType()            // "undefined" typeof 直接返回

getType(function(){}) // "function" typeof能判断,因此首字母小写

getType(/123/g)      //"RegExp" toString返回