强制类型转换
抽象操作(仅供内部使用)
ToString
null -> "null"
undefined -> "undefined"
boolean -> "true"
number -> "1", 对于较大的数会转成科学计数法 "1.07e21"
bigint -> "1",较大的树也会显示数字状态
symbol -> 'Symbol(a)'
object -> 如果有自定义toString方法,则调用对应方法。否则返回内部属性[[class]]的值,也就是[object Object]
数组toString是修改后的,会把所有元素用“,”连接
JSON字符串化
通过调用JSON.stringify将JSON安全的JSON数据转换成字符串。如果值为基本数据类型,字符串将会被转变成"content"(包含两端引号的字符串),其他基本数据类型用到了toString方法。
JSON安全
json值不安全的有:undefined, symbol, bigint, function, 循环引用
undefined, symbol, function -> 会自动忽略(如果是在数组中时,将会返回null来占位以保持元素相对位置不会改变)
Date -> "2024-07-03T09:06:48.743Z"
RegExp -> "{}",所有的都是空对象
循环引用 -> 直接报错 TypeError: Converting circular structure to JSON
BigInt -> 直接报错 TypeError: Do not know how to serialize a BigInt
如果对象定义了toJSON方法,那么会在字符串化之前调用这个方法。该方法没有入参数,不要使用箭头函数,否者无法用this定位到对象。返回值将会被传入JSON.stringify中进行序列化。(对象 -> toJSON -> JSON.stringify)
JSON.stringify允许传入三个参数。第二个可选(replacer)参数用于过滤出哪些键值对需要被序列化,可以传入数组或函数,函数返回undefined将会被过滤掉。第三个参数(space)是指定锁进字符。
replacer是函数的时候,会传入两个参数key和value。第一次调用key为""表示最顶层,此时value为object本身。函数如果返回undefined,该key将不会出现在序列化结果里。如果返回的是对象,将会递归调用replacer(深度优先),返回值将会出现在序列化结果里。
ToNumber
true -> 1
false -> 0
null -> 0
''、' '、'\n' -> 0
[] -> 0
undefined、其他 -> NaN
(ps. typeof NaN === true)
非基本数据类型 -(ToPrimitive)-> 基本数据类型 -(尝试转换)->
ToPrimitive
(通过内部操作 DefaultValue)检查该值是否有 valueOf() 方法。 如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString() 的返回值(如果存在)来进行强制类型转换。
如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。
调用时机
Number(x)
+x:一元操作符
- -"3.14":中间有空格来表示两次反转,如果没有空格表示递减操作
ToBoolean
假值(会被强制转换成false)包含以下内容:
undefined
null
false
+0, -0, NaN
""
其他的均为真值
假值对象(无需了解)
不是封装了假值的对象,这些都是真值(理论上所有的对象都是真值)
假值对象看起来和普通对象并无二致(都有属性,等等),但将它们强制类型转换为布尔 值时结果为 false。最常见的例子是 document.all。
显式转换
~ 运算符:返回补码
调用ToNumber转换成数字
调用ToInt32转换成32位数字(和强制转换类似效果)
对每一个二进制位进行取反
0 | -0;
0 | NaN;
0 | Infinity;
0 | -Infinity; // 0
以上这些特殊数字无法以 32 位格式呈现(因为它们来自 64 位 IEEE 754 标准,参见第 2 章),因此 ToInt32 返回 0。
~x 大致等同于 -(x+1)。所以如果~x === 0,那么x === -1。
-1 是一个“哨位值”,哨位值是那些在各个类型中(这里是数字)被赋予了特殊含义的值。 在 C 语言中我们用 -1 来代表函数执行失败,用大于等于 0 的值来代表函数执行成功。
! 运算符
强制转换成boolean值
取反
ParseInt vs Number
ParseInt 允许转换的字符串中含有字母,他会从左向右依次进行转换,遇到非数字的部分停下来。如ParseInt('342a') === 342,Object.is(ParseInt('a234'), NaN)。
Number不允许转换的字符串中包含其他字符,否则将会返回NaN。
向 parseInt 或者 parseFloatFloat 传递非字符串的时候会被隐式转换成字符串。先调用 valueOf,再调用toString
parseInt 可以传递两个参数,第二个参数是指定进制。如果不指定的话,ES5之前程序将会根据第一个参数自动决定用什么进制。
第一个字母是x,X:默认16进制
第一个是0:默认8进制(08、09开头属于无效参数,因为8、9不是八进制数)
0b:二进制
ES6及以后如果不特意指定第二个参数,都默认10进制。
隐式转换
加号(+)的执行
非原始类型的调用 ToPrimitive 转换成原始类型
两边有string的话,执行字符串拼接
两边都为boolean或者number的话执行加法运算
减号(-)、乘号(*)、除号(/)的执行
会把两边都转换成数字,执行数学运算。非原始类型的调用 ToPrimitive 转换成原始类型。
any -> Boolean 转换时机
if( __ )
for( .. ; __ ; .. )
while( __ )
__ ? .. : ..
__ || __ || __
__ && __ && __
符号的强制类型转换
符号类型强制转换没有问题,隐式转换会报错
Symbol -> Number
var s1 = Symbol( "cool" );
String( s1 ); // "Symbol(cool)"
var s2 = Symbol( "not cool" );
s2 + ""; // TypeError
宽松相等(==)和严格相等(===)
== 允许在相等比较中进行强制类型转换,而 === 不允许
宽松相等(==)
值的类型相同
如果两边值的类型相同,就仅比较它们是否相等。但是注意:
NaN != NaN
+0 == -0
对象只要地址一样就相等,否则不等(和 === 工作原理一致)
值的类型不相同
首先进行隐式类型转换,成为相同类型之后再执行比较。
不同类型值转换规则
String == Number:【正反相同】字符串转换成数字然后进行比较
Any == Boolean:【正反相同】先将Boolean转换成Number类型
Null == Undefined:【正反相同】true(与自身也相等,除开这几种情况其他均为false)
Object == 基础数据类型:先把对象转换成原始类型(ToPrimitive),再执行比较
使用 == 实现比较执行规则
有boolean先转换成number
复杂数据类型和基本类型的先用toPrimitive,两个都是复杂类型就直接比较地址
symbol转换成string
如果string == number的话,将string转成number再进行比较
“<”和“>”
首先调用toPrimitive转换成基本数据类型
如果两侧有number,就尝试把另一边也转成number
两边都是字符串就从头挨个字符比较顺序
语法
表达式和语句
语句都有一个结果值,在 chrome 的控制台执行语句时展示的值就是,当执行多个语句/执行代码块时,只展示最后一行语句的结果值。
有些语法会屏蔽语句的返回值,例如 var。
某些语句的结果值无法在常规情况下捕捉,例如 if 语句。此时可以利用 eval 方法执行,返回值就是语句的结果值。
ES7 规范有一项“do 表达式”(do expression)提案,能够在这种情况下代替 eval 方法。暂时还没有被采纳。
运算符优先级(左关联)
()
&&
||
... ? ... : ...