参考: https://juejin.cn/post/6844903854882947080#heading-1
ECMAScript标准规定了7种数据类型,其把这7种数据类型又分为两种:原始类型和对象类型。
原始类型
Null:只包含一个值:null
Undefined:只包含一个值:undefined
Boolean:包含两个值:true和false
Number:整数或浮点数,还有一些特殊值(-Infinity、+Infinity、NaN)
String:一串表示文本值的字符序列
Symbol:一种实例是唯一且不可改变的数据类型
对象类型
Object:除了常用的Object,Array、Function等都属于特殊的对象
一、类型转换
JavaScript是弱类型的语言,所以类型转换发生非常频繁。
类型转换分为两种:隐式转换即程序自动进行的类型转换,强制转换即我们手动进行的类型转换。
以下是可能发生隐式类型转换的几个场景,以及如何转换:
1.1 类型转换规则
转换前类型 | 转换前值 | 调转换后(Boolean) | 调转换后(Number) | 调转换后(String) |
---|---|---|---|---|
Boolean | true | - | 1 | “true” |
Boolean | false | - | 0 | “false” |
Number | 123 | true | - | “123” |
Number | Infinity | true | - | “Infinity” |
Number | 0 | false | - | “0” |
Number | NaN | false | - | “NaN” |
String | “” | false | 0 | - |
String | “123” | true | 123 | - |
String | “123Apple” | true | NaN | - |
String | “Apple” | true | NaN | - |
Symbol | Symbol() | true | TypeError | TypeError |
Null | null | false | 0 | “null” |
Undefined | undefined | false | NaN | “undefined” |
Function | function(){} | true | NaN | “function(){}” |
Object | {} | true | NaN | [object Object] |
Array | [] | true | 0 | “” |
Array | [“apple”] | true | NaN | “apple” |
Array | [“123”,“Apple”] | true | NaN | “123,apple” |
1.2 if语句和逻辑语句
在if
语句和逻辑语句中,如果只有单个变量,会先将变量转换为Boolean
值,只有下面几种情况会转换成false
,其余被转换成true:
null
undefined
''
NaN
0
false
1.3 运算数学符
在对各种非Number
类型运用数学运算符(- * /)
时,会先将非Number
类型转换为Number
类型;
1 - true // 0
1 - null // 1
1 * undefined // NaN
2 * ['5'] // 10
注意:+
是个例外,执行+
操作符时:
(1). 当一侧为String类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。
(2). 当一侧为Number类型,另一侧为原始类型,则将原始类型转换为Number类型。
(3). 当一侧为Number类型,另一侧为引用类型,将引用类型和Number类型转换成字符串后拼接。
123 + '123' // 123123 (规则1)
123 + null // 123 (规则2)
123 + true // 124 (规则2)
123 + {} // 123[object Object] (规则3)
1.4. ==
使用 ==
时,若两侧类型相同,则比较结果和===
相同,否则会发生隐式转换;使用==
时发生的转换可以分为几种不同的情况(只考虑两侧类型不同):
(1).NaN
NaN
和其他任何类型比较永远返回false
(包括和他自己)。
NaN = = NaN; // false
(2).Boolean
Boolean
和其他任何类型比较,会首先被转换为Number
类型。
true == 1 // true
true == '2' // false
true == ['1'] // true
true == ['2'] // false
这里注意一个可能会弄混的点:undefined、null
和Boolean
比较,虽然undefined、null
和false
都很容易被想象成假值,但是他们比较结果是false
,原因是false
首先被转换成0
:
undefined == false // false
null == false // false
(3).String和Number
String
和Number
比较,先将String
转换为Number
类型。
123 == ‘123’ // true
‘’ == 0 // true
(4).null和undefined
null == undefined
比较结果是true
,除此之外,null、undefined
和其他任何结果的比较值都为false
。
null == undefined // true
null == ‘’ // false
null == 0 // false
null == false // false
undefined == ’ ’ // false
undefined == 0 // false
undefined == false // false
(5).原始类型和引用类型
当原始类型和引用类型做比较时,对象类型会依照ToPrimitive
规则转换为原始类型:
'[object Object]' == {} // true
'1,2,3' == [1, 2, 3] // true
来看看下面这个比较:
[] == ![] // true
!
的优先级高于==
,![]
首先会被转换为false
,然后根据上面第二点,false
转换成Number
类型0
,左侧[]
转换为0
,两侧比较相等。
[null] == false // true
[undefined] == false // true
根据数组的ToPrimitive
规则,数组元素为null
或undefined
时,该元素被当做空字符串处理,所以[null]、[undefined]
都会被转换为0
。
总之,推荐使用===
来判断两个值是否相等。
三、判断JavaScript数据类型的方式
3.1. typeof
适用场景:typeof
操作符可以准确判断一个变量是否为下面几个原始类型:
typeof 'apple' // string
typeof 123 // number
typeof true // boolean
typeof Symbol() // symbol
typeof undefined // undefined
还可以用它来判断函数类型:
typeof function(){} // function
不适用场景:
当使用typeof
来判断引用类型时你会发现,除函数外所有的引用类型都会被判定为object
:
typeof [] // object
typeof {} // object
typeof new Date() // object
typeof /^\d*$/; // object
typeof null === 'object'
这是在JavaScript初版就流传下来的bug,后面由于修改会造成大量的兼容问题就一直没有被修复。
3.2. instanceof
instanceof
操作符可以帮助我们判断引用类型具体是什么类型的对象:
[] instanceof Array // true
new Date() instanceof Date // true
new RegExp() instanceof RegExp // true
此处涉及到原型链的几条规则:
1.所有引用类型都具有对象特性,即可以自由扩展属性
2.所有引用类型都具有一个__proto__
(隐式原型)属性,是一个普通对象
3.所有的函数都具有prototype
(显式原型)属性,也是一个普通对象
4.所有引用类型__proto__
值指向它构造函数的prototype
5.当试图得到一个对象的属性时,如果变量本身没有这个属性,则会去他的__proto__
中去找
[] instanceof Array
实际上是判断Array.prototype
是否在[]
的原型链上。
所以,使用instanceof
来检测数据类型,不会很准确,这不是它设计的初衷:
[] instanceof Object // true
function(){} instanceof Object // true
另外,使用instanceof也不能检测基本数据类型,所以instanceof并不是一个很好的选择。
3.3.toString
每一个引用类型都有
toString
方法,默认情况下,toString()
方法被每个Object
对象继承。如果此方法在自定义对象中未被覆盖,toString()
返回"[object type]"
,其中type是对象的类型。
const obj = {};
obj.toString() // [object Object]
我们可以直接调用Object
原型上未被覆盖的toString()
方法,使用call
来改变this
指向来达到我们想要的效果。
调用 | 结果 |
---|---|
Object.prototype.toString.call(true) | [object Boolean] |
Object.prototype.toString.call(123) | [object Number] |
Object.prototype.toString.call(‘apple’) | [object String] |
Object.prototype.toString.call(null) | [object Null] |
Object.prototype.toString.call(undefined) | [object Undefined] |
Object.prototype.toString.call({}) | [object Object] |
Object.prototype.toString.call([]) | [object Array] |
Object.prototype.toString.call(function(){}) | [object Function] |
Object.prototype.toString.call(Symbol()) | [object Symbol] |
Object.prototype.toString.call(new Error()) | [object Error] |
Object.prototype.toString.call(new RegExp()) | [object RegExp] |
Object.prototype.toString.call(new document) | [object RegExp] |
Object.prototype.toString.call(Math) | [object Math] |
Object.prototype.toString.call(JSON) | [object JSON] |
Object.prototype.toString.call(window) | [object global] |