深刻理解 JavaScript 中的类型和类型判断问题

也许你曾被 js 中的类型判断搞的晕头转向的,下面咱们来从头梳理一下 js 中的类型判断问题。javascript

基本类型

JavaScript 中的基本类型有 6 种:html

  • null
  • undefined
  • boolean
  • number
  • string
  • symbol

还有 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') 不会。

宽松相等和严格相等

你应该会常常听到不少规范要求你不要用 == 而用 ===,那么这两个相等之间有什么区别呢。简单的说来就是 == 会发生隐式类型转化,而 === 不会。

下面是 == 隐式类型转换的规则:

github.com/aniiantt/kn…

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,即便它不是。

参考资料

相关文章
相关标签/搜索