Javascript 由于各种各样的原因,在判断一个变量的数据类型方面一直存在着一些问题,其中最典型的问题恐怕就是 typeof null 会返回 object 了吧。因此在这里简单的总结一下判断数据类型时常见的陷阱,以及正确的处理姿势。


javascript 数据类型

MDN 数据类型

数据类型

这里先谈一下 javascript 这门语言的数据类型。javascript 中有七种数据类型,其中有六种简单数据类型,一种复杂数据类型。

六种简单数据类型

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Symbol (ECMAScript 6 新定义)

复杂数据类型

Object 是唯一的复杂数据类型。 Object Array Function 这些引用类型值最终都可以归结为 Object 复杂数据类型。


各种陷阱

typeof 的陷阱

MDN typeof

typeof 是用来检测变量数据类型的操作符,对一个值使用 typeof 操作符可能会返回下列某个字符串

  • "undefined" --- 如果这个值未定义
  • "string" --- 如果这个值是字符串
  • "boolean" --- 如果这个值是布尔类型值
  • "number" --- 如果这个值是数值
  • "object" --- 如果这个值是对象或者 null
  • "function" --- 如果这个值是函数

1. Object 对象检测的陷阱

function isObj(obj) {
if (typeof obj === 'object') {
return 'It is object';
}
return 'It is not object';
}

这个函数的本意是想检测传入的参数是否是 Object 对象。但是这个函数其实是非常不安全的。

比如

var a = [1, 2, 3];
isObj(a); // 'It is object' var b = null;
isObj(b); // 'It is object'

这样明显是不对的,因为 typeof []typeof null 都是是会返回 'object'的。

2. Array 对象检测的陷阱

typeof [] //  'object'

上面说到了对一个数组使用 typeof 操作符也是会返回 'object',因此 typeof 并不能判断数组对象的类型

instanceof 的陷阱 与 基本包装类型

1. instanceof

MDN instanceof

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。

2. 基本包装类

《Javascript》高级程序设计 第五章第六节 基本包装类型

javascript 为了方便操作基本类型值,ECMAscript 提供了3个特殊的引用类型:Boolean、Number 和 String。

每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。

var s1 = "some text";
var s2 = s1.substring(2);

上面的代码中,先创建了一个字符串保存在了变量 s1,字符串当然是基本类型值。但是在下一行中我们又调用了 s1 的方法。我们知道基本类型值不是对象,理论上它不应该拥有方法(但它们确实有方法)。其实,为了让我们实现这种直观的操作,后台已经帮助我们完成了一系列的操作。当我们在第二行代码中访问 s1 变量时,访问过程处于读取模式,而在读取模式中访问字符串时,后台都会自动完成下列处理。

  1. 创建 String 类型的一个实例;
  2. 在实例上调用指定的方法;
  3. 销毁这个实例。

可以将以上三个步骤想像成是执行了下列代码

var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;

3. instanceof 判断基本类型值的陷阱

上面提到基本包装类,就是为了说明 instanceof 这个陷阱

var str = 'text';
str instanceof String; // false

本来我也是想当然的认为 str instanceof String 会使 str 变量处于读取模式,自动建立基本包装类。但是根据上述代码所体现表象来看,instanceof 运算符是直接访问的变量的原始值。

因此 instanceof 并不能用来判断五种基本类型值

4. instanceof 判断 Object类型的陷阱

这里先说一下,用 instanceof 判断 Array 类型基本上是非常ok的

var arr = [1, 2, 3];
arr instanceof Array; // true

但是 instanceof 却不能安全的判断 Object 类型,因为 Array 构造函数是继承自 Object 对象的,因此在 arr 变量上是可以访问到 Object 的 prototype 属性的。如下例所示:

var arr = [1, 2, 3];
arr instanceof Object; // true
// 会返回 true ,是因为 Object 构造函数的 prototype 属性存在与 arr 这个数组实例的原型链上。

一个高效但危险的变量类型判断方法

用对象的 constructor 来判断对象类型

stack overflow 上有人做了实验,说是目前运算最快的判断变量类型的方式。

function cstor(variable) {
var cst = variable.constructor; switch (cst) {
case Number:
return 'Number'
case String:
return 'String'
case Boolean:
return 'Boolean'
case Array:
return 'Array'
case Object:
return 'Object'
}
}

上面是一个判断变量类型的方法,工作的非常高效完美。但是用 constructor 判断变量类型有一个致命的缺陷,就是当检测 null 或者 undefined 类型的 constructor 属性时,js会报错!

也就是说下面代码会报错!

cstor(null);  // 若传入 null 或者 undefined 作为参数时
// cstor 函数第一行就会报错,因为 null 和 undefined 根本就没有 constructor 属性

因此我们在利用变量的 constructor 属性来判断变量类型时,必须要先保证变量有 不会是 null 或者 undefined。

改造以上函数如下:

function cstor(variable) {
if (variable === null || variable === undefined) {
return 'Null or Undefined';
} var cst = variable.constructor; switch (cst) {
case Number:
return 'Number'
case String:
return 'String'
case Boolean:
return 'Boolean'
case Array:
return 'Array'
case Object:
return 'Object'
}
}

所以说使用 constructor 来判断对象类型时要无时无刻不伴随着排除 null 和 undefined 的干扰,否则就会产生致命的问题,因此本人并不推荐。


正确判断变量类型的姿势

一个万金油方法 Object.prototype.toString.call()

MDN reference

Object.prototype.toString.call(variable) 用这个方法来判断变量类型目前是最可靠的了,它总能返回正确的值。

该方法返回 "[object type]", 其中type是对象类型。

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

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

Object.prototype.toString.call({});  //  "[object Object]"

Object.prototype.toString.call(123);  //  "[object Number]"

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

Object.prototype.toString.call(false);  //  "[object Boolean]"

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

String Boolean Number Undefined 四种基本类型的判断

除了 Null 之外的这四种基本类型值,都可以用 typeof 操作符很好的进行判断处理。

typeof 'abc'  // "string"

typeof false  // "boolean"

typeof 123  // "number"

typeof undefined  // "undefined"

Null 类型的判断

除了 Object.prototype.toString.call(null) 之外,目前最好的方法就是用 variable === null 全等来判断了。

var a = null;

if (a === null) {
console.log('a is null');
} // a is null

检测变量是否是一个 Array 数组

typeof [] 会返回 object 因此明显是不能够用 typeof 操作符进行数组类型判断的。目前常用的方法有以下几种

1. Object.prototype.toString.call()

万金油方法是一种。

2. ECMAscript5 新增 Array.isArray()

Array.isArray([]);   // true

3. instanceof 运算符

MDN reference

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。

因此可以检测一个对象的原型链中是否存在 Array 构造函数的 prototype 属性来判断是不是数组。

[] instanceof Array   // true

检测变量是否是一个 Object 对象

typeof 和 instanceof 都不能安全的判断变量是否是 Object 对象。

目前判断变量是否是对象的最安全的方法就只有 Object.prototype.toString.call() 了。


作者博客:pspgbhu http://www.cnblogs.com/pspgbhu/

作者 Githubhttps://github.com/pspgbhu

欢迎转载,但请注明出处,谢谢!

Javascript 判断变量类型的陷阱 与 正确的处理方式的更多相关文章

  1. JavaScript判断变量类型

    使用JavaScript变量时是无法判断出一个变量是0 还是“”的 这时可用typeof()来判断变量是string 还是number来区分0和“”, typeof(undefined) == 'un ...

  2. PHP判断变量类型和类型转换的三种方式

    前言: PHP 在变量定义中不需要(不支持)明确的类型定义.变量类型是根据使用该变量的上下文所决定的.所以,在面对页码跳转.数值计算等严格的格式需求时,就要对变量进行类型转换. 举例如下: $foo ...

  3. JavaScript 变量克隆和判断变量类型

    一.变量克隆 在js中经常会遇到将一个变量赋值给一个新的变量这种情况,这对于基本类型很容易去实现,直接通过等号赋值就可以了,对于引用类型就不能这样了.(注:像函数,正则也可以直接通过等号赋值) 这里我 ...

  4. JavaScript判断浏览器类型及版本

    JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本.JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一 ...

  5. 使用javascript判断浏览器类型

    之前在项目中遇到过要针对不同浏览器做不同的一些js或者css操作,后来某个朋友也突然问到这个问题,所以,整理了一下,在这里留个笔记,方便以后使用. 使用javascript判断浏览器类型: funct ...

  6. JavaScript判断对象类型及节点类型、节点名称和节点值

    一.JavaScript判断对象类型 1.可以使用typeof函数判断对象类型 function checkObject1(){ var str="str"; console.lo ...

  7. 16. js 判断变量类型,包括ES6 新类型Symbol

    相信大家在开发中遇到需要判断变量类型的问题,js变量按存储类型可分为值类型和引用类型,值类型包括Undefined.String.Number.Boolean,引用类型包括object.Array.F ...

  8. php判断变量类型

    php判断变量类型 一.总结 一句话总结: gettype()函数:gettype(1);返回的是integer is_array():is系列函数 1.PHP empty.isset.isnull的 ...

  9. JavaScript判断图片是否加载完成的三种方式

    JavaScript判断图片是否加载完成的三种方式 有时需要获取图片的尺寸,这需要在图片加载完成以后才可以.有三种方式实现,下面一一介绍. 一.load事件 1 2 3 4 5 6 7 8 9 10 ...

随机推荐

  1. (入门篇 NettyNIO开发指南)第五章-分隔符和定长解码器使用

    TCP    以流的方式进行数据传输上层的应用协议为了对消息进行区分,往往采用如下4种方式. (1)消息长度固定,累计读取到长度总和为定长LEN 的报文后,就认为读取到了一个完整的消息,将计数器置位, ...

  2. 003---hibernate主要接口介绍

    Hibernate可以访问JNDI.JDBC.JTA JNDI(Java名称和目录接口):主要管理我们对象,特别是EJB应用,它会把所有EJB应用加入到JNDI这棵树上,Tomcat连接池也是把对象注 ...

  3. 在容器中利用Nginx-proxy实现多域名的自动反向代理、免费SSL证书

    在个人的小项目或者测试环境中,配置反向代理显得十分繁琐,而借助 Nginx-proxy 的镜像,即使是小白,也能快速实现域名转发. 1.域名.IP自动转发 在开始之前,首先黑进了自家的路由器,将某个域 ...

  4. python基础 - 01

    python 变量名 在python中的变量命名,与其他语言大体相似,变量的命名规则如下: 变量名是数字.字母.下划线的任意组合 变量名的第一个字符不能是数字 系统的关键字不能设置为变量名    Ti ...

  5. Windows 修改电脑属性(一)

    修改电脑属性里的注册信息 修改电脑属性的注册信息 运行注册表的方法:开始→运行→regedit→确定 1.CPU型号可以注册表编辑器中定位到下面的位置: HKEY_LOCAL_MACHINE\HARD ...

  6. 使用 libdvm.so 内部函数dvm* 加载 dex

    首先要清楚,odex只是对代码段(我将dex文件与elf文件类比,大家都将执行文件分成不同的段)作优化,而其它用于类反射信息的段都应用原来的dex,所以odex文件内部还包含了一个dex. 打开一个d ...

  7. SQLalchemy模块用法

    安装 pip install sqlalchemy #!/usr/bin/env python # -*- coding:utf-8 -*- # 加载模块 from sqlalchemy.ext.de ...

  8. Linux 多个JDK的版本 脚本切换

    这里是在CentOS 系统下配置多个版本之间的切换 1.到官网下载jdk7和jdk8 地址:http://www.oracle.com/technetwork/cn/java/javase/downl ...

  9. 最短路径Floyd算法【图文详解】

    Floyd算法 1.定义概览 Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被 ...

  10. MySQL优化之表结构优化的5大建议(数据类型选择讲的很好)

    殊不知,在N年前被奉为"圣经"的数据库设计3范式早就已经不完全适用了.这里我整理了一些比较常见的数据库表结构设计方面的优化技巧,希望对大家有用. 由于MySQL数据库是基于行(Ro ...