在 ECMAScript 规范中,共定义了 7 种数据类型,分为 基本类型 和 引用类型 两大类,如下所示:
    基本类型:String、Number、Boolean、Symbol、Undefined、Null
    引用类型:Object
基本类型也称为简单类型,由于其占据空间固定,是简单的数据段,为了便于提升变量查询速度,将其存储在栈中,即按值访问。
引用类型也称为复杂类型,由于其值的大小会改变,所以不能将其存放在栈中,否则会降低变量查询速度,因此,其值存储在堆(heap)中,而存储在变量处的值,是一个指针,指向存储对象的内存处,即按址访问。引用类型除 Object 外,还包括 Function 、Array、RegExp、Date 等等。
鉴于 ECMAScript 是松散类型的,因此需要有一种手段来检测给定变量的数据类型。对于这个问题,JavaScript 也提供了多种方法,但遗憾的是,不同的方法得到的结果参差不齐。

下面介绍常用的4种方法,并对各个方法存在的问题进行简单的分析。
1、typeof

typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:number、boolean、symbol、string、object、undefined、function 等。
> typeof ''; // string 有效
> typeof 1; // number 有效
> typeof Symbol(); // symbol 有效
> typeof true; //boolean 有效
> typeof undefined; //undefined 有效
> typeof null; //object 无效
> typeof [] ; //object 无效
> typeof new Function(); // function 有效
> typeof new Date(); //object 无效
> typeof new RegExp(); //object 无效

有些时候,typeof 操作符会返回一些令人迷惑但技术上却正确的值:
    对于基本类型,除 null 以外,均可以返回正确的结果。
    对于引用类型,除 function 以外,一律返回 object 类型。
    对于 null ,返回 object 类型。
    对于 function 返回  function 类型。
其中,null 有属于自己的数据类型 Null , 引用类型中的 数组、日期、正则 也都有属于自己的具体类型,而 typeof 对于这些类型的处理,只返回了处于其原型链最顶端的 Object 类型,没有错,但不是想要的结果。

2、instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型,用一段伪代码来模拟其内部执行过程:    
instanceof (A,B) = {
    var L = A.__proto__;
    var R = B.prototype;
    if(L === R) {
        // A的内部属性 __proto__ 指向 B 的原型对象
        return true;
    }
    return false;
}

从上述过程可以看出,当 A 的 __proto__ 指向 B 的 prototype 时,就认为 A 就是 B 的实例,再来看几个例子:
[] instanceof Array; // true
{} instanceof Object;// true
new Date() instanceof Date;// true
 
function Person(){};
new Person() instanceof Person;// true
 
[] instanceof Object; // true
new Date() instanceof Object;// true
new Person instanceof Object;// true

发现,虽然 instanceof 能够判断出 [ ] 是Array的实例,但它认为 [ ] 也是Object的实例,为什么呢?来分析一下 [ ]、Array、Object 三者之间的关系:
从 instanceof 能够判断出 [ ].__proto__  指向 Array.prototype,而 Array.prototype.__proto__ 又指向了Object.prototype,最终 Object.prototype.__proto__ 指向了null,标志着原型链的结束。因此,[]、Array、Object 就在内部形成了一条原型链:

__proto__([]) --> __proto__(Array.prototype) --> __proto__(Object.prototype) --> null

从原型链可以看出,[] 的 __proto__  直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,[] 就是Object的实例。依次类推,类似的 new Date()、new Person() 也会形成一条对应的原型链 。因此,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。
    
> var iframe = document.createElement('iframe');
> document.body.appendChild(iframe);
> xArray = window.frames[0].Array;
> var arr = new xArray(1,2,3); // [1,2,3]
> arr instanceof Array; // false

针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型,而不区分该对象在哪个环境中创建。
    
if (Array.isArray(value)){
   //对数组执行某些操作
}

Array.isArray() 本质上检测的是对象的 [[Class]] 值,[[Class]] 是对象的一个内部属性,里面包含了对象的类型信息,其格式为 [object Xxx] ,Xxx 就是对应的具体类型 。对于数组而言,[[Class]] 的值就是 [object Array] 。

3、constructor
当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。如下所示:
> function F(){}
< undefined
> F.prototype
< Object {}
    >constructor: function F()
    >__proto__: Object
>

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

可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。
同样,JavaScript 中的内置对象在内部构建时也是这样做的:
> ''.constructor == String
< true
> new Number(1).constructor == Number
< true
> true.constructor == Boolean
< true
> new Function().constructor == Function
< true
> new Date().constructor == Date
< true
> new Error().constructor == Error
< true
> [].constructor == Array
< true
> document.constructor == HTMLDocument
< true
> window.constructor == window
< true
>

细节问题:
    1. null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
    2. 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
> function F(){}
< undefined
> F.prototype = {a: 'xxxx'}
< Object {a: "xxxx"}
> var f = new F()
< undefined
> f.constructor == F
< false
> f.constructor
< function Object() {[native code]}
>

为什么变成了 Object?
因为 prototype 被重新赋值的是一个 { }, { } 是 new Object() 的字面量,因此 new Object() 会将 Object 原型上的 constructor 传递给 { },也就是 Object 本身。因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。

4、toString
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。
对于 Object 对象,直接调用 toString()  就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。    
> Object.prototype.toString.call('') ;   // [object String]
> Object.prototype.toString.call(1) ;    // [object Number]
> Object.prototype.toString.call(true) ; // [object Boolean]
> Object.prototype.toString.call(Symbol()); //[object Symbol]
> 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 的引用

JS数据类型的判断的更多相关文章

  1. js数据类型的判断方法

    判断js中的数据类型有一下几种方法:typeof.instanceof. constructor. prototype. $.type()/jquery.type(),接下来主要比较一下这几种方法的异 ...

  2. js数据类型及判断数据类型

    众所周知,js有7种数据类型 1. null 2. undefined 3. boolean 4. number 5. string 6. 引用类型(object.array.function) 7. ...

  3. 前端基础——js数据类型及判断方法

    一.数据类型 我们通常熟知的数据类型有六种,包括5种基本数据类型(Number, String, Boolean, Undefined, Null)和一种引用数据类型(Object).ES6又新增了一 ...

  4. js数据类型之判断

    js有几种类型,具体是:字符串(String).数字(Number).布尔(Boolean).数组(Array).对象(Object).空(Null).未定义(Undefined). js提供了typ ...

  5. js 数据类型的判断

    1. typeof 运算符 typeof 可以判断基本数据类型: typeof 123; // "number" typeof 'abc'; // "string&quo ...

  6. 实现 js 数据类型的判断函数type

    type = (obj) => { const pass1 = typeof obj if (pass1 != 'object') return pass1 const pass2 = obj ...

  7. js中的数据类型和判断数据类型

    js中的数据类型和判断数据类型 基本数据类型,六大基本数据类型:字符串(String).数字(Number).布尔(Boolean).对象(Object).空(Null).未定义(Undefined) ...

  8. 正确判断js数据类型 总结记录

    正确判断js数据类型 总结记录 判断js中的数据类型有一下几种方法:typeof.instanceof. constructor. prototype. 三方库. js六大数据类型 number: 数 ...

  9. 浏览器解析js和type判断数据类型

    ### 浏览器解析: - 1.当浏览器(内核.引擎)解析和渲染js的时候,会给js提供一个运行的环境,这个环境叫做“全局作用域(后端global / 客服端window scope)” - 2.代码自 ...

随机推荐

  1. unity中 UGUI的按下、拖动接口事件的实现

    using UnityEngine; using System.Collections.Generic; using DG.Tweening; using UnityEngine.EventSyste ...

  2. mysql一些常用配置

    清空表: truncate table 表名 让id重1开始 alter table `test` auto_increment=1

  3. python笔记13-文件读写

    1.打开文件 f=open('a.txt','a+',encoding='utf-8')#f代表的是文件对象,叫句柄 f.seek(0)把文件指针到最前 文件打开模式有3种: 1:w写模式,它是不能读 ...

  4. Python 自动发送邮件

    简单邮件传输协议(SMTP)是一种协议,用于在邮件服务器之间发送电子邮件和路由电子邮件.Python提供smtplib模块,该模块定义了一个SMTP客户端会话对象,可用于使用SMTP或ESMTP侦听器 ...

  5. 团队项目(MVP------新能源无线充电管理网站)(总结)

    经过了几个月的学习时间与团队的磨合以及一系列的困难之后,我们mvp小组一起完成了这个项目,内心也是十分激动和有成就感的.其实一开始基础并不好,很多都不知道,但是通过在慕课网上的学习以及老师严厉地督促下 ...

  6. 外网ssh内网Linux服务器holer实现

    外网ssh访问内网linux 内网的主机上安装了Linux系统,只能在局域网内访问,怎样从公网也能ssh访问本地Linux服务器? 本文将介绍使用holer实现的具体步骤. 1. 准备工作 1.1 安 ...

  7. 剑指Offer 16. 合并两个排序的链表 (链表)

    题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 题目地址 https://www.nowcoder.com/practice/d8b6b4358 ...

  8. SONObjetc和String Map Bean互转,JSONArray和String List互转

    import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; i ...

  9. python+flask开发小白第二天

    使用VSCode编译python web页面 1.先从最基础的说起吧,关于VSCode的使用: 运行python程序与运行java,c,c++程序一样,需要新建一个文件,第一个文件建议不要新建在本地的 ...

  10. JavaBasic_12

    类 抽象:对象的共性 类:产生对象的模板(对于属性来讲,类规定有没有属性以及属性类型,具体的属性值因对象的不同而不同) 数据类型:数据集及基于这个数据集的操作 类:我们自定义类型(定义成员变量,基于数 ...