关于JS数据类型检测的多种方式总结

目录
  • 背景
  • 判断数据类型的手段有哪些?
    • 1. 用typeof判断基础数据类型:
    • 2. 用instanceof判断对象数据类型
    • 3. 用contructor属性
    • 4. toString方法
    • 5. 用Array.isArray判断数组
    • 6.区分ArrayLike与Array
    • 7.判断一个对象是否是纯对象(or普通对象)
    • 8. NaN如何检测,Number.isNaN与isNaN有啥区别
    • 9. 鸭式类型检测法
  • 总结

背景

总所周知,js是一门动态的弱类型脚本语言,其采用动态的类型系统以及基于原型的继承方式。

缺乏类型的静态约束,这意味着数据类型导致的程序错误并不能在编译阶段及时发现,要想写出健壮的代码,就必须在运行时各种的check&兼容,所以能够熟练准确的检测数据类型成为掌握这门语言最重要的基础之一。

判断数据类型的手段有哪些?

总的来说大致有以下几种:typeof、instanceof、Object.prototype.toString、constructor、鸭式类型、及针对特定类型的检测方法Array.isArray(),Number.isNaN(),虽然方法很多,但他们的使用场景有所不同。

1. 用typeof判断基础数据类型:

返回值有undefined、string、number、boolean、object、function、symbol七种。

可以看出,typeof作为官方提供的类型检测操作符,在检测undefined、string、boolean、symbol这些基本数据类型及function方面是十分靠谱的。表现拉垮的地方主要在于

1) 不能对具体对象类型(Array、Date、regExp)进行区分。
2) typeof null === 'object' // 竟然是true。。。。

缺陷 2)可以避免,在判断对象引用类型时多判断一句即可,typeof x === 'object' &&  x !== null。但是不能区分对象的具体类型,确实是个很大痛点。

2. 用instanceof判断对象数据类型

此运算符用于检测某个构造函数的prototype是否出现在目标对象的原型链上。

这是一种预测的检测方式,并不会像typeof一样直接将数据类型以字符串的方式进行返回,而是你需要预判对象类型的构造函数,最终返回一个boolean值。

检测规则其实从命名就可以看出,判断实例是否是由某个构造函数所创建的,那么知道了原理,现在动手实现一个属于自己的instanceof。

function myInstanceof(target,constructor){
  const baseType = ['string', 'number','boolean','undefined','symbol']
    if(baseType.includes(typeof(target))) { return false }
    //原型链其实就是个对象组成的链表,遍历这个链表,
  let prototype = Object.getPrototypeOf(target);
    while(prototype){
        //一旦链上有对象有符合,就返回true
      if(prototype === constructor.prototype){
        return true
      }else{
        prototype = Object.getPrototypeOf(prototype)
      }
    }
    return false
}
console.log(myInstanceof([],Array))

在js里,可以从广义上认为万物源于对象,因为实例虽然是通过构造函数创建的,但是构造函数本身只是没有感情的生产机器,实例的灵魂和性格(公共属性和方法)都是共享自构造函数的prototype属性指向的那个原型对象,而且原型对象都是纯对象,纯对象又是由Object构造函数创建的,那么就会造成下边这种后果。

对于数组,遍历原型链上的对象,Array.prototype Object.prototype都会出现。

并且,对字面量方式创建的基本数据类型无法进行判断。比如

如何弥补上边的缺陷呢,答案是可以在上边特殊的场景中采用下边的constructor代替instanceof。

3. 用contructor属性

首先先明确。constructor是原型上的属性,实例继承自原型,所以实例上也能直接访问此属性。
首先看下contructor的通用性表现

意外的表现不错,除了null、undefined,有contructor属性的基础(包装)类型或者对象类型都能准确判断。

能准确区分Array|Object 因为它没有instanceof那样会遍历整条原型链,只是在实例身上进行判断。但也有个致命的缺陷,实例上的这一属性太容易被修改了,一旦修改,这个方法就没有意义了。

4. toString方法

首先,js的对象类型或者基础类型的包装对象都有一个toString方法。继承自Object.prototype.toString(),调用会返回对应类型的字符串标记"[object Type]"。

这个方法有种乱拳打死老师傅,无心插柳柳成荫的感觉,本来的作用只是得到一个表示该对象的字符串,现在用在js类型检测上,表现简直不要太好,针对基础类型及对象类型表现都非常不错,如果非要说个缺点,只能说返回的字符串有点复杂,使用不太方便,现在让我们动手简化一下。

先写一个简版

function isType(type,value){
    return Object.prototype.toString.call(value) === `[object ${type}]`
}
console.log(isType('Array',[]))
console.log(isType('Number',1))

这样使用也不太方便,‘Array' ‘Number'这样的类型参数,很容易拼写错误,所以希望方法可以预设参数,并且希望构造一个函数工厂,调用返回类似于isArray这样的函数。在IDE中函数名相比字符串会拥有更好的代码提示,不容易拼写错误。

function isType(type){
    return function(value){
        return Object.prototype.toString.call(value) === `[object ${type}]`
    }
}

const isArray = isType('Array')
const isNumber = isType('Number')
console.log(isArray([]),isNumber(1))

这里运用了高阶函数的思想,保留参数+返回一个新的函数,那么可以想到js里bind除了可以绑定this,也有保留参数+返回新函数的功能,用在这里也很合适。

function isType(type,value){
    return Object.prototype.toString.call(value) === `[object ${type}]`
}

const isArray = isType.bind(null,'Array')
const isNumber = isType.bind(null,'Number')
console.log(isArray([]),isNumber(1))

更进一步,用参数柯里化的思想改造一波

function isType(type,value){
    return Object.prototype.toString.call(value) === `[object ${type}]`
}
function curring (fn,...args1){
    let len = fn.length;
    return function(...args2){
        const args = args1.concat(args2);
        if(args.length < len){
            return curring(fn,...args)
        }else{
            return fn(...args)
        }
    }
}
const isArray = curring(isType,'Array')
const isNumber = curring(isType,'Number')
console.log(isArray([]),isNumber(1))

最后,丰富一下支持的类型,大功告成。

const types = [
    'Null',
    'Undefined',
    'String',
    'Number',
    'Boolean',
    'Object',
    'Array',
    'Date',
    'Function',
    'RegExp',
    'Symbol',
    'Math',
]
const checkTypeUtil = {}
types.forEach((type)=>{
    checkTypeUtil[`is${type}`] = curring(isType,type)
})
export {
 checkTypeUtil
}
console.log(checkTypeUtil.isArray([]))

5. 用Array.isArray判断数组

上边提到 instanceof可以用来检测数组,但是这在iframe创建的多window环境中,因为window全局环境需要隔离,所以Array和Array.prototype在每个窗口中必须是不同的,所以iframeA.Array.prototype ≠ iframeB.Array.prototype,所以 iframeA.arr instanceof iframeB.Array必定是返回false,这是小概率的事件,但是在使用iframe的场景里,互相传值,也是非常可能发生的。使用ES6提供的Array.isArray就没有这个问题,可以准确判断数组。

可以这样 pollify

if (!Array.isArray) {
  Array.isArray = function(x) {
    return Object.prototype.toString.call(x) === '[object Array]';
  };
}

6.区分ArrayLike与Array

类数组的定义是:

  • 拥有length属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理
  • 不具有数组所具有的方法
function isLikeArray(x){
    if(!(typeof x === 'object' && x !== null)){
        return false
    }
    return typeof x.length === 'number' && x.length >= 0 && !Array.isArray(x)
}

类数组可以用Array.from Array.prototype.slice.call(val)来转换为真正的数组。

7.判断一个对象是否是纯对象(or普通对象)

纯对象的定义:特指通过一下三种方式创建的对象

  • new Object
  • 对象字面量创建 {}
  • Object.create(null)

jquery、lodash源码都是采用下边的方法来检测

const funcToString = Function.prototype.toString
const objectCtorString = funcToString.call(Object)

function isPlainObject(value){
    // 先用toString先排除其他数据类型
    if(!value || !Object.prototype.toString.call(value) === "[object Object]"){
        return false
    }
    const proto = Object.getPrototypeOf(value)
    if(proto === null){//兼容Object.create(null)这样创建的对象
        return true
    }
    const Ctor = Object.prototype.hasOwnProperty.call(proto,'constructor') && proto.constructor;
    if(typeof Ctor !== 'function'){
        return false
    }
    // 这里通过字符串判断构造函数是否是Object,而不是直接使用instanceof,是为了避免上边提到的 多window环境Object不同的问题
    if(funcToString.call(Ctor) === objectCtorString){
        return true
    }
    return false
}
console.log(isPlainObject(Object.create(null)))
console.log(isPlainObject(new Object))
console.log(isPlainObject({a:1}))

8. NaN如何检测,Number.isNaN与isNaN有啥区别

结论:Number.isNaN会严格的判断传入的值是否是直接等于NaN。

isNaN则会先进行Number()转换,然后再进行是否是NaN的判断。

9. 鸭式类型检测法

其实上边利用constuctor判断数据类型,就是采用了这种方法。判断一个动物是不是鸭子,那么通过看起来像鸭子,叫起来像鸭子这样简单的经验判断就可大致进行判断。

比如判断一个对象是不是一个Promise,就可以这样

function isPromise(x){
    if(!(x instanceof Promise)){
        return false
    }
    return typeof x.then === 'function'
}

总结

到此这篇关于JS数据类型检测的文章就介绍到这了,更多相关JS数据类型检测内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一文带你走进js-数据类型与数据结构的世界

    目录 2. 数据类型 2.1 原始类型(6 种原始类型,使用 typeof 运算符检查) 2.2 null 与 Object 3. 原始值 3.1 原始值基本概念 3.2 各类型说明 1. 什么叫动态类型 JavaScript 是一种弱类型或者说动态语言. 我们不用提前声明变量的类型,在程序运行过程中,类型会被自动确定. 这也意味着你可以使用同一个变量保存不同类型的数据 let a; // 初始不给任何类型 a = 11; // a此时是number类型 a = "二大爷"; // a

  • 关于js typeof 与 instanceof 判断数据类型区别及开发使用

    目录 1.typeof 操作符 2.instanceof 操作符 3.typeof 和 instanceof 的区别以及开发中的使用建议 前言: 日常的开发中,我们经常会遇到判断一个变量的数据类型或者该变量是否为空值的情况,你是如何去选择判断类型的操作符的?本文来总结记录一下我们开发人员必须掌握的关于 typeof 和 instanceof 的知识点以及在开发中的使用建议,同时在面试过程中也经常会遇到这样的问题. 1.typeof 操作符 typeof 操作符,确定任意变量的数据类型,确切说,它

  • JS中的四种数据类型判断方法

    目录 1.typeof 2.instanceof 3.constructor 4.toString() 本文总结了四种判断方法: 1.typeof typeof是一个运算符,其有两种使用方式:(1)typeof(表达式); (2)typeof 变量名;返回值是一个字符串,用来说明变量的数据类型;所以可以用此来判断number, string, object, boolean, function, undefined, symbol 这七种类型,每种情况返回的内容如下表所示: // 字符串 con

  • JavaScript的基础语法和数据类型详解

    目录 引入JavaScript 1.内部标签 2.外部引入 基础语法 数据类型 number 字符串 布尔值 逻辑运算 比较运算符 数组 对象 流程控制 Map和Set iterator 总结 引入JavaScript 1.内部标签 <script> alert("hello world"); </script> 2.外部引入 <script src="js/abc.js"></script> 基础语法 定义变量 &l

  • javascript七大数据类型详解

    目录 前言: 详细介绍: 练习: 总结 前言: 各大语言都会有基本类型如python,java,c系列等,基本类型的存在是不可或缺的,它就好似我们生活中算数中的整数,汉字中的中文,它就是来表示这些的,想想还是蛮重要的,那么js有常见的五大基本数据类型有:String,Number,Boolean,Undefined,Null,还有两个常见复杂类型Object,Symbol. 详细介绍: 序号 类型 昵称 范围 常见方法 介绍 1 String 字符串 .toString() 将对象转换为Stri

  • javascript基础之数据类型详解

    目录 1.数据类型 1.1 为什么需要数据类型 1.2 变量的数据类型 1.3 数据类型分类 2. 简单数据类型(基本数据类型) 2.1 数字型Number 1. 数字型进制 2.数字型范围 2.2 字符串型 String 1.字符串转义符 2.字符串长度 2.3 布尔型 Boolean 3.数据类型转换 3.1转换为字符串 3.2 转换为数字型 隐式转换 3.3 转换为布尔型 总结 1.数据类型 1.1 为什么需要数据类型 在计算机,不同的数据所占的存储空间是不同的,为了便于把数据分成所需内存

  • 利用JS判断数据类型的四种方法

    目录 前言 1.typeof 2.instanceof 3.constructor 细节问题: 4.toString 总结 前言 Javascript 中的数据类型判断其实是一个JavaScript非常基础问题,但不论是日常实际编程过程中和还是面试时,这都是一个非常常见的问题. 在 ECMAScript 规范中,共定义了 7 种数据类型,分为 基本类型 和 引用类型 两大类,如下所示: 基本类型:String.Number.Boolean.Symbol.Undefined.Null 引用类型:O

  • 关于JS数据类型检测的多种方式总结

    目录 背景 判断数据类型的手段有哪些? 1. 用typeof判断基础数据类型: 2. 用instanceof判断对象数据类型 3. 用contructor属性 4. toString方法 5. 用Array.isArray判断数组 6.区分ArrayLike与Array 7.判断一个对象是否是纯对象(or普通对象) 8. NaN如何检测,Number.isNaN与isNaN有啥区别 9. 鸭式类型检测法 总结 背景 总所周知,js是一门动态的弱类型脚本语言,其采用动态的类型系统以及基于原型的继承

  • js数据类型检测总结

    在js中,有四种用于检测数据类型的方式,分别是: typeof 用来检测数据类型的运算符 instanceof 检测一个实例是否属于某个类 constructor 构造函数 Object.prototype.toString.call() 原型链上的Object对象的toString方法 下面我们就来分别介绍一下上面四种方法的适用场景和局限性. typeof 用来检测数据类型的运算符 使用typeof检测数据类型,返回值是字符串格式.能够返回的数据类型 是:"number","

  • JavaScript数据类型检测实现方法详解

    目录 一.typeof 二.instanceof 三.Object.prototype.toString.call() 面试问题 一.typeof 优点:能快速判断基本数据类型,除了 Null: 缺点:不能判别 Object.Array.Null ,都返回 object:判别引用类型除函数显示 function外,其他显示为 object console.log(typeof 55); // number console.log(typeof true); // boolean console.

  • JS中检测数据类型的几种方式及优缺点小结

    1.typeof 用来检测数据类型的运算符 typeof value 返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number"."string"."boolean"."undefined"."object"."function" 局限性: 1)typeof null ->"object" 2)检测的不管是数组还是正则都返回的是"ob

  • js学习总结_基于数据类型检测的四种方式(必看)

    1.typeof 用来检测数据类型的运算符 console.log(typeof 12)//Number 使用typeof检测数据类型,首先返回的都是字符串 ,其次字符串中包含了对应的数据类型 例如:"number"."string"."boolean"."undefined"."function"."object" console.log(typeof typeof function(

  • 利用JS十分钟判断数组中存在元素的多种方式

    前言 在前端开发中,经常会遇到要判断数组中是否存在某个元素.其实判断的方式有很多种,我们一个一个来了解下. 我们先来定义一个数组: const arr = [ 13, false, 'abcd', undefined, 13, null, NaN, [1, 2], { a: 123 }, () => Date.now(), new Date('2021/03/04'), new RegExp('abc', 'ig'), Symbol('sym'), ]; 在这个数组中,我们包含了好几种类型:nu

  • JS中多种方式创建对象详解

    1.内置对象创建 var girl=new Object(); girl.name='hxl'; console.log(typeof girl); 2.工厂模式,寄生构造函数模式 function Person(name){ var p=new Object();//内部进行实例化 p.name=name; p.say=function(){ console.log('my name is '+ p.name); } return p;//注:一定要返回 } var girl=Person('

  • 多种方式实现js图片预览

    先贴代码,之后完善: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="Generator" content="EditPlus®"> <meta name="Author" content=""> <meta

  • vue引用js文件的多种方式(推荐)

    1.vue-cli webpack全局引入jquery (1) 首先 npm install jquery --save (--save 的意思是将模块安装到项目目录下,并在package文件的dependencies节点写入依赖.) (2)在webpack.base.conf.js里加入 var webpack = require("webpack") (3)在module.exports的最后加入 plugins: [ new webpack.optimize.CommonsChu

  • js 创建对象的多种方式与优缺点小结

    目录 早期创建方式 工厂模式 构造函数模式 构造函数模式优化 原型模式 构造函数和原型模式组合 动态原型模式 寄生构造函数模式 稳妥构造函数模式 早期创建方式 var obj = new Object() obj.name ='xxx' obj.age = 18 或使用对象字面量 var o1 = { name: 'xxx', say: () => {} } var o2 = { name: 'xxx', say: () => {} } 缺点:使用同一个接口创建很多对象,会产生大量重复代码 工

随机推荐