查漏补缺!JS中数据类型的判断
面试季马上就要过去了,很多小伙伴也是在努力的抓住金九银十的小尾巴,今天来说一说面试中常见的一个问题:js中如何判断数据类型?
众所周知,JavaScript是一门弱类型的语言,它允许不兼容的类型进行运算,但有些场景使得我们不得不对数据类型做一个规范,如此就需要对数据类型进行判断。
可能你会回答,我一般用typeof来进行判断,可是面试官会问你,那如果遇到值为null的情况下typeof会返回什么?对于初入江湖的新手来说可能在这个时候就败下阵来,没错,我当初也是这样,判断数据为null或者undefined不是用if-else来判断的吗?
带着疑问,接下来我们就来看一下如何判断数据类型:

typeof
在此引用MDN的说明:
typeof 操作符返回一个字符串,表示未经计算的操作数的类型。
下表总结了 typeof 可能的返回值:

示例:
console.log(typeof 333); // numberconsole.log(typeof true); // booleanconsole.log(typeof 'aaa'); // stringconsole.log(typeof []); // object []数组的数据类型在 typeof 中被解释为 objectconsole.log(typeof function(){}); // functionconsole.log(typeof {}); // objectconsole.log(typeof undefined); // undefinedconsole.log(typeof null); // object null 的数据类型被 typeof 解释为 object
null// JavaScript 诞生以来便如此typeof null === 'object';
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"。
曾有一个 ECMAScript 的修复提案(通过选择性加入的方式),但被拒绝了。该提案会导致 typeof null === 'null'。
在 ECMAScript 2015 之前,typeof 总能保证对任何所给的操作数返回一个字符串。即便是没有声明的标识符,typeof 也能返回 'undefined'。使用 typeof 永远不会抛出错误。
但在加入了块级作用域的 let 和 const 之后,在其被声明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 ReferenceError。块作用域变量在块的头部处于“暂存死区”,直至其被初始化,在这期间,访问变量将会引发错误。
typeof undeclaredVariable === 'undefined';typeof newLetVariable; // ReferenceErrortypeof newConstVariable; // ReferenceErrortypeof newClass; // ReferenceErrorlet newLetVariable;const newConstVariable = 'hello';class newClass{};

instanceof
依然引用MDN的说明:
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
示例:
console.log(333 instanceof Number); // falseconsole.log(true instanceof Boolean); // falseconsole.log('aaa' instanceof String); // falseconsole.log([] instanceof Array); // trueconsole.log(function(){} instanceof Function); // trueconsole.log({} instanceof Object); // true
以上结果显示,直接的字面量值判断数据类型,只有引用数据类型(Array,Function,Object)被精准判断,其他(Number,Boolean,String)字面值不能被instanceof精准判断。
需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,这时原表达式的值就会成为 false。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 __proto__ 伪属性,是可以实现的。比如执行 obj.__proto__ = {} 之后,obj instanceof Foo 就会返回 false 了。
到这里大家可能已经观察到了,为什么我在上面的示例中没有用instanceof来判断null和undefined,我们在浏览器中输出一下看是什么结果:

浏览器在这里报错了,它认为null,undefined不是构造器。

Object.prototype.toString.call()
最后,留到最后的才是最好的,我们来看一下终极大招:
Object.prototype.toString.call()
MDN中说明:
toString() 方法返回一个表示该对象的字符串。
每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中 type 是对象的类型。以下代码说明了这一点:
var o = new Object();o.toString(); // returns [object Object]
可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为 thisArg。
var toString = Object.prototype.toString;toString.call(333); // [object Number]toString.call("aaa"); // [object String]toString.call(true); // [object Boolean]toString.call([]); // [object Array]toString.call(function(){}); // [object Function]toString.call({}); // [object Object]toString.call(undefined); // [object Undefined]toString.call(null); // [object Null]// 甚至于js的内置对象也能被精确判断toString.call(new Date); // [object Date]toString.call(new String); // [object String]toString.call(Math); // [object Math]
相信到这里你已经知道该如何回答面试官的问题了吧
