JavaScript之判断数据类型

Javascript 中的数据类型判断实际上是一个JavaScript很是基础问题,但不管是平常实际编程过程当中和仍是面试时,这都是一个很是常见的问题。javascript

不少人被问到如何判断一个变量数据类型这个问题时,大概都能回答上几种方法,可是一深刻的问题各类判断方法的区别、优劣、局限、原理,却一时半会理不清楚了,因此就借这一篇文章,复习一下JavaScript中这个基础但很容易出错的问题。html

typeof

咱们都使用 typeof 是用来判断数据类型的命令, 在常规的场景中足以应付数据类型判断的需求:前端

var obj = {
   name: 'zhangxiang'
};

function foo() {
    console.log('this is a function');
}

var arr = [1,2,3];

console.log(typeof 1);  // number
console.log(typeof '1');  //string
console.log(typeof true);  //boolean
console.log(typeof null); //object
console.log(typeof undefined); //undefined
console.log(typeof obj); //object
console.log(typeof foo);  //function
console.log(typeof arr);   //object

由结果可知typeof能够测试出numberstringbooleanundefinedfunction,而对于null数组对象,typeof均检测出为object,不能进一步判断它们的类型。java

因此通常来讲, typeof 会使用在比较简单的场景, 好比你几乎能够肯定数据是哪一类数据而后稍微加以区分的时候.node

顺便吐槽一句,typeof null === 'object' 是JavaScript的一个bug,那么多个版本还一直不愿改过来,什么鬼...面试

instanceof

instanceof 其实适合用于判断自定义的类实例对象, 而不是用来判断原生的数据类型, 举个例子:编程

// a.html
<script>
  var a = [1,2,3];
</script>
// main.html
<iframe src="a.html"></iframe>

<script>
  var frame = window.frames[0];
  var a = frame.a;
  console.log(a instanceof Array);  // false
  console.log(a.contructor === Array);  //false
  console.log(a instanceof frame.Array); // true
</script>

是什么缘由致使上面的结果呢? 其实 iframe 之间不会共享原型链, 由于他们有独立的执行环境, 因此 frame a 中的数组 a 不会是本执行环境的实例对象. 经过特性嗅探一样不靠谱, 像经过 contructorsegmentfault

Sort, slice 等等的特有的数组(或者其余数据类型)方法或属性, 万一对象中也有 sort, slice 属性, 就会发生误判. 因此最靠谱的方法是使用 Object.prototype.toString 方法.数组

Object.prototype.toString

在任何值上调用 Object 原生的 toString() 方法,都会返回一个 [object NativeConstructorName] 格式的字符串。
须要注意的是,可是它不能检测非原生构造函数的构造函数名。浏览器

function foo(){};

Object.prototype.toString.call(1);  '[object Number]'
Object.prototype.toString.call('1'); '[object String]'
Object.prototype.toString.call(NaN); '[object Number]'
Object.prototype.toString.call(foo);  '[object Function]'
Object.prototype.toString.call([1,2,3]); '[object Array]'
Object.prototype.toString.call(undefined); '[object Undefined]'
Object.prototype.toString.call(null); '[object Null]'
Object.prototype.toString.call(true); '[object Boolean]'

每一个类在内部都有一个 [[Class]] 属性,这个属性中就指定了上述字符串中的构造函数名。

Object.prototype.toString 的原理是当调用的时候, 就取值内部的 [[Class]] 属性值, 而后拼接成 '[object ' + [[Class]] + ']' 这样的字符串并返回. 而后咱们使用 call 方法来获取任何值的数据类型.

constructor

console.log(bool.constructor === Boolean);// true
console.log(num.constructor === Number);// true
console.log(str.constructor === String);// true
console.log(arr.constructor === Array);// true
console.log(obj.constructor === Object);// true
console.log(fun.constructor === Function);// true

console.log(haoxl.constructor === Student);// false
console.log(haoxl.constructor === Person);// true

undefined和null没有contructor属性

constructor不能判断undefined和null,而且使用它是不安全的,由于contructor的指向是能够改变的

有用的数据类型判断函数

Array.isArray()
用于肯定传递的值是不是一个Array。若是对象是Array,则返回true,不然为false。

Array.isArray([1, 2, 3]);  
// true
Array.isArray({foo: 123}); 
// false
Array.isArray("foobar");   
// false
Array.isArray(undefined);  
// false

isNaN()和Number.isNaN
isNaN()函数用来肯定一个值是否为 NaN。
注:isNaN函数内包含一些很是有趣的规则;
你也可使用 ECMAScript 2015 中定义的Number.isNaN()来判断。

与 JavaScript 中其余的值不一样, NaN不能经过相等操做符(== 和 ===)来判断 ,由于 NaN == NaN 和 NaN === NaN 都会返回 false。 所以,isNaN 就颇有必要了。

isNaN(NaN); // true

OK, 成功了,看似很完美,可是接着看如下例子

isNaN('A String'); // true

isNaN(undefined); // true

isNaN({}); // true

会发现,很明显不是 NaN 的 value 也被误判成 NaN 了。

这个BUG已经存在了20年,从JavaScript最开始就一直存在。很明显当初的设计者,在设计isNaN()的时候,局限了在 "Not a Number" 这一字面意思上了:只要不是number就会返回 true。

因而 ES6 为了弥补这一BUG(而不是修正,由于isNaN存在时间太长,有可能不少功能都是基于这个BUG之上的)引入了 Number.isNaN().

Number.isNaN(NaN); // true

Number.isNaN('A String'); // false

Number.isNaN(undefined); // false

Number.isNaN({}); // false

没有ES6的状况下,能够采用如下polyfill

if (!Number.isNaN) {
  Number.isNaN = function(n) {
    return (
      typeof n === "number" &&
      window.isNaN( n )
    );
  };
}

判断是不是 DOM 元素

在实际项目里面, 有时或许咱们须要判断是不是 DOM 元素对象, 那么在判断的时候利用的是 DOM 对象特有的 nodeType 属性:

isElement: function(obj){
  return !!(obj && obj.nodeType === 1);
}

判断是不是对象

isObject: function(obj){
  var type = typeof obj;
  return type === 'function' || typeof === 'object' && obj !== null;
}

这里的对象是狭义的, 是一般所指的 key-value 型的集合, 或者是 function 函数而且不为 null.

判断是不是 arguments 对象

判断一个对象是否是 arguments 对象能够经过 Object.prototype.toString 来判断, 可是低版本的浏览器不支持, 他们返回的是 [object Object], 因此须要兼容:

isArguments: function(obj){
  return Object.prototype.toString.call(obj) === '[object Arguments]' || (obj != null && Object.hasOwnProperty.call(obj, 'callee'));
}

兼容作法原理是经过对象的 hasOwnProperty 方法来判断对象是否拥有 callee 属性从而判断是否是 arguments 对象.


参考:https://juejin.im/post/59c753...


推荐阅读:
【专题:JavaScript进阶之路】
JavaScript之深刻理解闭包
JavaScript之“use strict”
JavaScript之new运算符
JavaScript之call()理解
JavaScript之对象属性


我是Cloudy,年轻的前端攻城狮一枚,爱专研,爱技术,爱分享。
我的笔记,整理不易,感谢阅读、点赞和收藏。
文章有任何问题欢迎你们指出,也欢迎你们一块儿交流前端各类问题!
相关文章
相关标签/搜索