一、概述
判断js中的数据类型有一下几种方法:typeof、instanceof、 constructor、Object.prototype.toString。这篇文章来分析一下这几种方式底层原理,比较一下他们的区别。
二、typeof
typeof 是最常用的判断数据类型的方法,我们可以利用 typeof 来判断number, string, object, boolean, function, undefined, symbol 这七种类型。
底层原理: js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息,而typeof运算符就是通过一个存储变量的低位来判断数据类型的。
【注意】:
- 系统返回时返回的是相关类型的字符串,在作为判断条件的时候需要额外注意。
- 对于null使用typeof运算符,将会返回object。这个由于系统底层实现时,将null的机器码指定为全0,这样设置与对象数据类型的低位完全相同,导致typeof出现了误判。
- typeof并不是完全安全的运算符,在使用typeof运算符时,如果出现了暂时性死区,typeof运算符也会导致报错。
- 对于使用new构造函数,构造的基本数据类型,将被认定为对象。
实例:
// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // Despite being "Not-A-Number"
typeof Number('1') === 'number'; // Number tries to parse things into numbers
// Strings
typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // note that a number within a string is still typeof string
typeof (typeof 1) === 'string'; // typeof always returns a string
typeof String(1) === 'string'; // String converts anything into a string, safer than toString
// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() will convert values based on if they're truthy or falsy
typeof !!(1) === 'boolean';
// two calls of the ! (logical NOT) operator are equivalent to Boolean()
// Symbols
typeof Symbol() === 'symbol'
typeof Symbol('foo') === 'symbol'
typeof Symbol.iterator === 'symbol'
// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';
// Objects
typeof {a: 1} === 'object';
// use Array.isArray or Object.prototype.toString.call
// to differentiate regular objects from arrays
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
typeof /regex/ === 'object';
// See Regular expressions section for historical results
// The following are confusing, dangerous, and wasteful. Avoid them.
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';
// Functions
typeof function() {} === 'function';
typeof class C {} === 'function';
typeof Math.sin === 'function';
【注意】:
- Infinity,NaN会被判断成number
- 对于使用两次取反符合的变量,将会被判定为boolean
三、instanceof
其实 instanceof 主要的作用就是测试,是否某个构造函数的属性出现在对象的原型链中的任何位置。
底层原理: 只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false
【注意】:
- 需要判断的数据应该是对象,其他基本数据类型使用将返回false
- 使用new操作符,创建的对象,也可以进行判断
- 使用时注意大小写,系统构造函数的首字母都是大写
var newStr = new String('String created with constructor');
newStr instanceof String; // returns true
newStr instanceof Object; // returns true
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car('Honda', 'Accord', 1998);
mycar instanceof Car; // returns true
mycar instanceof Object; // returns true
if (!(mycar instanceof Car)) {
// Do something, like mycar = new Car(mycar)
}
【注意】:
- 使用instanceof判断数据不是特定构造函数的实例时,要添加括号。否则取反符合将和实例结合。
四、constructor
既然可以通过原型来判断数据类型,当然也可以使用变量上的constructor属性来判读具体数据类型。
var a = 5;
a.constructor == Number -----------> true;
var a = "123";
a.constructor == String -----------> true;
function A(){};
function B(){};
A.prototype = new B(); //A继承自B
var aObj = new A();
alert(aobj.constructor === B) -----------> true;
alert(aobj.constructor === A) -----------> false;
【注意】:
- 这种方法有一个很大缺陷,就是只能判断直接继承,不同判读间接继承
- 判断undefined,null时会报错
五、Object.prototype.toString
通过使用object原型toString方法打印队形类型。
var obj = {a:5};
Object.prototype.toString.call(obj);
问题: 同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为toString为Object的原型方法,而Array 、Function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(Function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。
【注意】:
- 通用方法,可以检测各种类型
- 但对于自建的自定义函数,将只能识别为object
- 在判定时,要严格依照输出字符串(’[object String]‘)进行判定