类型 & 值
内置类型
JavaScript 一共有 7 中内置类型:null, undefined, boolean, number, string, object 和 Symbol
使用 typeof 可以检查它们的类型;但是 typeof null 返回 'object' (这是一个 bug)
因此,如果要想检测一个值为 null 需要使用:
var a = null
!a && typeof a === 'object' // true,(所有对象都是真值,所以 !a 返回 true)强制类型转换
我们在讨论强制类型转换时,转换的结果都是基本类型。
我们可以把强制类型转换分为隐式和显式两种,例如下面:
var a = 42
var b = a + '' // 隐式
var c = String(a) // 显式当然,隐式和显式只是相对的;并没有绝对的划分
抽象值操作
ToString
null返回'null',undefined返回'undefined',true返回'true'数字返回对应的字符串,极小或极大的使用指数形式
普通对象,除非自定义;否则返回内部
[[Class]]属性的值数组的
toString()方法被重定义了,它将所有元素以 “ , ” 分割再拼成字符串
toString() 可以被显式调用,也可以在需要字符串化时自动调用。
JSON.stringify()
我们还可以对对象使用 JSON.stringify() 来将它序列化为字符串。
在这个过程中,也是用到了 ToString 的上述规则。不过他们还是不一样的!
值得一提的是,在 JSON.stringify() 的过程中,会忽略一些 “不安全的 JSON 值” ,即:undefined, function, symbol 以及循环引用(对象之间互相引用)。
这些 “不安全的 JSON 值” 在序列化时,会被自动忽略;如果他们存在某一个数组中,那么则会在对应位置返回 null
JSON.stringify({
a: function foo() { },
b: [undefined, function bar(){}]
})
// '{"b":[null,null]}'可以通过定义 toJSON() 方法来返回一个安全的 JSON 值
var obj = {
a: 'foo',
b: function bar() { }
}
// 返回一个指定的安全的对象
obj.toJSON = function() {
return {
a: this.a
}
}注意,这里介绍 JSON.stringify() 并不是因为它也是一个强制类型转换。而是在它其中,也运用到了 ToString 的一些规则。
例如当遇到对象中某一个属性是 null ,它就会应用规则将其转换为 'null'
ToNumber
true转换为 1,false转换为 0undefined转换为NaNnull转换为 0字符串先尝试转换,若失败则变为
NaN对象先转换为相应的基本类型;若非数字,再应用上面的规则。将对象转换为基本类型会使用
ToPrimitive操作,具体的过程为:检查是否有
valueOf方法;若有,返回基本类型值如果没有则使用
toString()的返回值,再执行上述规则如果
valueOf和toString()均不会返回基本类型,则产生TypeError
ToBoolean
在 JavaScript 当中,数字 1, 0 不等同于 true 和 false 。我们虽然可以使用强制类型转换将 1 变为 true ,(反之亦然)。但是他们不能画等号!
转换为 boolean 时只需要检查这个值是否为 “假值” ,以下都是假值
undefinednullfalse''+0, -0,
NaN
以上 “假值” 将会转换为 false;除此之外,所有值都转换为 true
对象,数组都是真值,即便是对假值进行封装:
!!new Boolean(false) // true,!! 应用了隐式强制类型转换
!![] // true
!!function() {} // true显式强制类型转换
字符串和数字之间转换
使用内置的 String() 和 Number() 函数可以实现字符串和数字之间的相互转换。
注意不需要使用 new 构造调用它们,因此并不创建封装对象。
String(0) // '0'
Number('0') // 0String() 和 Number() 分别遵循上面介绍的 ToString 和 ToNumber 抽象值操作的规则。
此外,还可以通过以下的方法来进行 “显式转换”
0..toString() // '0' 注意要两个 .
+'0' // 0除了 Number 可以将字符串转换为数字,还可以使用 parseInt() 方法;但他们之间有点区别:
Number('0px') // NaN
parseInt('0px') // 0Number 不支持解析字符串中的一部分,而 parseInt() 在遇到非数字字符时可以截止,如果失败再返回 NaN
parseInt 只支持传入字符串,他不能解析其他类型的值。它会先将传入的值转换为字符串,再继续下去。
当然还有
parseFloat将值转换为浮点数
转换为 boolean
可以使用 Boolean() 内置函数来显式地将值转换为 boolean;不过一种更常用的方式是使用 !!
!!0 // false
!!'' // false
!![] // true隐式强制类型转换
字符串和数字之间的转换
在使用 + 运算的时候,会发生隐式转换;因为 + 既可以执行加法运算,也能进行字符串拼接。他的具体规则如下:
如果其中的一个操作符是字符串,或者可以通过
ToPrimitive抽象操作转换为字符串;那么就执行拼接否则,执行数字加法运算
ToPrimitive 操作的具体过程如下:
先调用其
valueOf()获得基本类型值如果没有,再调用
toString()方法
这个操作和
ToNumber类似
利用上述特性,我们通常会使用 0 + '' 这种操作来将数字转换为字符串。
此外,还可以使用 a - 0 , a * 1 , a / 1 来隐式转换为数字。
boolean 转换为数字
true 会转换为 1,false 会转换为 0
Number(!![]) // 1, 先使用 !! 保证传入的是 boolean;否则可能会返回 NaN转换为 boolean
在以下几种情况下,都会发生隐式的转换,将值转换为 boolean:
if 语句
for 语句的第二个表达式
while, do ... while 语句
? : 三目运算符
|| && 左边的操作数
|| && 操作符的实际行为可能和你想象中的不太一样,因为他们的返回值并不一定是 boolean
相反,他们会返回左右两个操作数中的某一个;具体操作如下:
首先对左边的操作数进行判断,此时可能会发生隐式转换将值变为 boolean
对于
||如果判断为true则返回左边的操作数;否则返回右边对于
&&如果判断为true则返回右边的操作数;否则返回左边
== 和 ===
两者的区别在于:== 允许在比较时进行强制类型转换,而 === 不允许。
也就是说,如果两个值的类型相同;那么他们使用 == 或者 === 效果是相同的。
== 规则
如果两个值的类型相同,则直接比较他们是否相等;不过以下两种情况注意:
NaN不等于NaN+0 等于 -0
如果是两个对象,那么之比较他们的引用是否相同。
但是,如果两个值的类型不同;那么就会发生隐式强制类型转换,将其中一个或者两个转换为相同类型再进行比较。下面将分别介绍不同的情况:
字符串和数字比较
将两者之间的字符串转换为数字,然后再执行比较。
'0' == 0 // true转换使用
ToNumber抽象操作
其他类型和 boolean 比较
不要和 Boolean() 强制转换时判断假值混淆,以下结果可能出乎你的预料:
'0' == true // false,'0' 虽然不是假值,但是结果并不是 true正确的过程是:会将其中的 boolean 值转换为 Number,然后再比较
因此,下述表达式就能返回 true
'0' == false // false 被先转换成了 0,再比较 '0' == 0这里有一个坑就是你很容易以为这里会对 ”非 boolean“ 的那个值做一次隐式转换将其变为 boolean。但事实完全相反,我们会将那个 boolean 值转换为 1 或者 0;然后在执行比较!
但是你可以通过 !! 进行显式转换:
var a = '42'
a == true // false, 实际比较的是 '42' == 1
!!a == true // truenull 和 undefined 比较
在 == 时,null 和 undefined 是相等的
null == undefined // true注意,只有 null 和 undefined 之间的 == 是返回 true 的,但是如果他们和其他 “假值" 进行比较,都不会返回 true :
null == 0 // false
undefined == '' // false
null == false // false对象和非对象之间比较
如果将一个对象(对象、数组、函数)和一个基本类型(string、number、boolean)进行比较,规则如下:
使用 ToPrimitive 抽象操作将对象转换为基本类型,再执行比较。
需要注意的是,如果非对象是一个 boolean;则会想将其转换为数字。
[42] == 42 // true抽象关系比较
在执行 a < b 判断的时候,如果两边类型不同;也会发生隐式转换
如果双方都是字符串,则按照字母顺序来比较
不然的话,双方都先调用
ToPrimitive,如果出现非字符串,则再统一执行ToNumber转换为数字后再比较
[42] < ['43'] // true,实际上是比较 '42' < '43'不过有些情况就比较特别了,注意仔细甄别:
var a = { n: 42 }
var b = { n: 43 }
a < b // false,实际上是比较 '[object Object]' < '[object Object]'
a == b // false,这里比较的只是引用;没有 ToPrimitive 操作哦然而,更让你意外的是:
a <= b // true这是因为,JavaScript 中 <= 操作其实是 !(a > b) ,因此上面的结果返回 true
另外需要指出:与 === 不同的是,关系比较没有 ”严格比较“。因此当两边类型不同时,会发生隐式转换。请尽量保证两边比较的类型时相同的!
Last updated