在 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; [] 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__  直接指向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 的引用。如下所示:

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

可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。

同样,JavaScript 中的内置对象在内部构建时也是这样做的:

细节问题:

1. null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。

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

为什么变成了 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数据类型 总结记录 判断js中的数据类型有一下几种方法:typeof.instanceof. constructor. prototype. 三方库. js六大数据类型 number: 数 ...

  2. 判断数组的方法/判断JS数据类型的四种方法

    参考文: 以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣Object.prototype.toString.call() . instanceof 以及 Array.isArray() h ...

  3. 判断JS数据类型的四种方法

    在 ECMAScript 规范中,共定义了 7 种数据类型,分为 基本类型 和 引用类型 两大类,如下所示: 基本类型:String.Number.Boolean.Symbol.Undefined.N ...

  4. 判断js数据类型的四种方法,以及各自的优缺点(转)

    转载地址:https://blog.csdn.net/lhjuejiang/article/details/79623973 数据类型分为基本类型和引用类型: 基本类型:String.Number.B ...

  5. 判断JS数据类型的几种方法

    原文转自http://www.cnblogs.com/onepixel/p/5126046.html! 说到数据类型,我们先说一下JavaScript 中常见的几种数据类型: 基本类型:string, ...

  6. 一个判断js数据类型的函数

    function judgeType(change) { if (arguments.length == 0) { return '0';//无参数传入 } if (change === null) ...

  7. js数据类型的检测总结,附面试题--封装一个函数,输入任意,输出他的类型

    一.javascript 中有几种类型的值 1.基本数据类型 : 包括 Undefined.Null.Boolean.Number.String.Symbol (ES6 新增,表示独一无二的值) 特点 ...

  8. 如何判断js中的数据类型?

    js六大数据类型:number.string.object.Boolean.null.undefined string: 由单引号或双引号来说明,如"string" number: ...

  9. 如何判断js中的数据类型

    如何判断js中的数据类型:typeof.instanceof. constructor. prototype方法比较 如何判断js中的类型呢,先举几个例子: var a = "iamstri ...

  10. [转]如何判断js中的数据类型

    原文地址:http://blog.sina.com.cn/s/blog_51048da70101grz6.html 如何判断js中的数据类型:typeof.instanceof. constructo ...

随机推荐

  1. PS设计非常漂亮酷炫的金色字体效果

    方法/步骤 1.在PS里面新建一个黑色背景 2.在图层上 写上比较粗一点的字. 3.鼠标右击图层,选择混合模式,进行调整. 选择颜色叠加,注意颜色是深黄色. 4.转移到混合模式里面的渐变叠加,渐变的颜 ...

  2. CRON表达式转换成中文

    目录 1,cron表达式生成器 2,cron表达式 一,结构 二.各字段的含义 三.常用表达式例子 3,转换成中文: 1,CronExpParserUtil 2,WeekEnum 3,测试: 4,测试 ...

  3. jmeter接口自动化-读取CSV文件执行测试用例

    一.在csv文件中编写好用例 首先在csv文件首行填写相关参数并编写测试用例.脚本可通过优先级参数控制执行哪些接口,通过请求方式执行不同端口下的接口,再读取csv文件时进行参数化. 二.设计测试脚本并 ...

  4. 3、k8s 核心实战

    7 kubernets核心实战 7.1 资源创建方式 命令行 yaml 7.2 namespace 名称空间来隔离资源 命令行方式 kubectl create ns hello kubectl de ...

  5. 华为&思科设备默认的路由协议优先级

    华为&思科设备默认的路由协议优先级 华为设备默认路由协议优先级 在华为的设备中,路由器分别定义了外部优先级和内部优先级. 外部优先级是指用户可以手工为各路由协议配置的优先级; 内部优先级不能被 ...

  6. 【杂项】瞎玩——suhr RIOT单块制作流程

    难得的周末,在家上课似乎丝毫没有轻松多少,然而专业课任务的ddl远在天边,上午赶赶进度似乎稍微闲下来了点,正好前几天找电路的时候看到一个非常nice的效果器制作网站,国内少有深入研究效果器电路的文章, ...

  7. Java语言打印空心菱形

    打印空心菱形如果一开始觉得比较困难的话,那么不妨分成几个步骤走完成菱形的打印. 1.先打印一个直角三角形(都是由星星组成的) 代码如下: import java.util.Scanner;//导入Sc ...

  8. OS复盘

    OS复盘 OS课设已经结课一段时间了,我也从其他课程中抽身出来,有了一段能够好好反思的时光. 说实话,完成OS课设的过程是很痛苦的,指导书蜻蜓点水.注释模棱两可.各种函数和文件调用杂乱无章.每次在完成 ...

  9. linux与windows互通

    https://www.cnblogs.com/zhouby/p/10724149.html

  10. sentinel监控数据持久化&本地测试

    官方文档  https://sentinelguard.io/zh-cn/ wiki: 在生产环境中使用-Sentinel 推荐方案:持久化到 时序数据库InfluxDB  : 结合Grafana 可 ...