也许你曾被 js 中的类型判断搞的晕头转向的,下面咱们来从头梳理一下 js 中的类型判断问题。javascript
JavaScript 中的基本类型有 6 种:html
还有 object。咱们能够经过 typeof 判断它们的类型,如:java
typeof null === 'object' // true
typeof undefined === 'undefined' // true
typeof 11 === 'number' // true
...
复制代码
其中 typeof null === 'object'
,因此并不能单纯的经过 typeof 判断 null ,这是 js 实现的问题。若是想判断是不是 null 的话,能够这样作:git
var a = null
// 复合判断
(!a && typeof a === 'object') // true
// 判断相等性
a === null // true
复制代码
还有一种状况就是,对于 function,它是 object 的子类型,可是 typeof function 会返回 function。github
typeof function(){} === 'function' // true
复制代码
因为 function 是一个 object,因此 function 能够拥有普通对象属性。它的内部有一个 [[Call]] 属性,使得它能够被调用。编程
可是判断 Array,就不能这样了,虽然 Array 也是 Object 的子类型。函数
typeof [] // object
复制代码
对于一个变量,若是它没有值的时候, typeof 会返回 undefined,不然则会返回它的值的类型。对于 undefined 咱们也能够直接判断相等性。ui
var a
a === undefined // true
复制代码
可是 undefined 是能够被赋值改写的。为了防范这种状况,咱们经过 void 运算符,进行操做,void __ 总会返回 undefinedspa
var a
a === void 0 // true
复制代码
对于一个未声明(undeclared)的变量,直接访问会报错,可是 typeof 依然会返回 undefinedprototype
nnnnnnnn // Uncaught ReferenceError: nnnnnnnn is not defined
typeof nnnnnnnn // undefined
复制代码
这个特性能够帮助咱们作一些防护性编程,由于直接 if(xxxxxx) 会抛错,可是 if(typeof xxxxxx !== 'undefined') 不会。
你应该会常常听到不少规范要求你不要用 == 而用 ===,那么这两个相等之间有什么区别呢。简单的说来就是 == 会发生隐式类型转化,而 === 不会。
下面是 == 隐式类型转换的规则:
www.ecma-international.org/ecma-262/5.…
== 有时候是会带来一些便利的,好比说 null 和 undefined 比较是 true。字符串和数字比较字符串会被转换数字再比较。
比较使人费解的地方是布尔值和其余类型比较,布尔值先被转换为数字 0 或者 1,再进行比较。对象和非对象之间比较,对象会被先转换为原始值。
或许你觉得 == 就应该照你想的那样工做,没有符合你的预期的话,你会以为是坑。但其实不是这样的,它有着一套完善的规则。个人意见是,若是你十分清楚了解隐式转换的规则的话,== 是彻底能够用的,你知道本身在干什么。不然使用 === 。
NaN 表示一个无效的数字,它依然是数字类型:
typeof NaN == 'number' // true
复制代码
若是你简单的经过 == 或 NaN 判断一个数字是不是 NaN:
NaN === NaN // false
NaN !== NaN // true
复制代码
这样是不行的!window 有一个全局的函数 isNaN 能够用来判断是不是 NaN,它的检查方式是判断一个值是否不是一个 NaN,也不是数字。也就是说:
isNaN('aaa') // true
isNaN(NaN) // true
isNaN(100) // false
isNaN('100') // false
复制代码
ES6 新增 Number.isNaN 会判断一个数字类型是不是 NaN:
Number.isNaN('aaa') // false
Number.isNaN(NaN) // true
Number.isNaN(100) // false
Number.isNaN('100') // false
复制代码
除此以外还有一个 Infinity -- 无穷大。 1 / 0 就是 ∞,Infinity。同理 isFinite 能够判断是否不是 Infinity,它一样有一个全局函数和一个 Number 类型的成员函数:
isFinite('aaa') // false
isFinite(NaN) // false
isFinite(Infinity) // false
isFinite('100') // true
isFinite(100) // true
Number.isFinite('aaa') // false
Number.isFinite(NaN) // false
Number.isFinite(100) // false
Number.isFinite('100') // false
Number.isFinite(100) // true
复制代码
对于零值,咱们来看看一下一个例子:
0 * -1 // -0
复制代码
+0 -0 的存在是在数学上合理的。但在程序中判断它们并非那么容易的,。好在 ES6 提供了 Object.is 函数,咱们能够用它来处理这些特殊值:
Object.is(0, -0) // false
Object.is(-0, -0) // false
Object.is(NaN, NaN) // true
Object.is(Infinity, Infinity) // true
Object.is(Infinity, -Infinity) // false
复制代码
详细见 developer.mozilla.org/zh-CN/docs/…
了解知道了这么多特殊值,那么咱们该怎么判断
如 RegExp Function Date Array 它们是 js 引擎自带的函数,那么该怎么判断它们的类型呢。
它们内部实现,有一个 [[class]] 属性,经过 Object.prototype.toString(...),能够间接访问到这个属性,从而实现类型判断:
Object.prototype.toString.call(/./) // [object RegExp]
Object.prototype.toString.call(() => {}) // [object Function]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call([]) // [object Array]
复制代码
你也可使用 instanceof 进行判断,如 [] instanceof Array === true
可是请注意,这样在跨宿主环境时会有问题的(如跨 iframe),由于两个不一样环境的 Array 对象并非同一个。
a instanceof Foo 能够用来判断 a 的整条原型链 [[prototype]] 中,是否有指向 Foo.prototype 的引用:
function Foo() {}; Foo.prototype.a = '';
function Bar() {}; Bar.prototype = new Foo(); Bar.prototype.b = '';
new Bar instanceof Foo // true
new Bar instanceof Bar // true
复制代码
可是若是你是采用 Object.create(...)
的方式建立原型链,那么 instanceof 就不是那么管用了,由于此时并不存在什么构造函数。
a.isPrototypeOf(b)
能够判断 a 是否出如今 b 的原型链中。 a instanceof Foo
能够用 Foo.prototype.isPrototypeOf(a)
代替,这样更清晰的显示了他们的关系。
Promise 中是经过检查返回结果是否具备 then 方法,来判断返回结果是不是一个 thenable 这样作很方便,但同时也很脆弱。咱们不得不注意要避免给对象加上 then 属性,否则你在 Promise 中返回这个对象的时候,会被看成一个 Promise,即便它不是。
参考资料