强制类型转换

种类

隐式类型转换

显式类型转换

发生时态

编译时

运行时

抽象操作(仅供内部使用)

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。

显式转换

~ 运算符:返回补码

  1. 调用ToNumber转换成数字

  2. 调用ToInt32转换成32位数字(和强制转换类似效果)

  3. 对每一个二进制位进行取反

     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 的值来代表函数执行成功。

! 运算符

  1. 强制转换成boolean值

  2. 取反

ParseInt vs Number

ParseInt 允许转换的字符串中含有字母,他会从左向右依次进行转换,遇到非数字的部分停下来。如ParseInt('342a') === 342,Object.is(ParseInt('a234'), NaN)。

Number不允许转换的字符串中包含其他字符,否则将会返回NaN。

向 parseInt 或者 parseFloatFloat 传递非字符串的时候会被隐式转换成字符串。先调用 valueOf,再调用toString

parseInt 可以传递两个参数,第二个参数是指定进制。如果不指定的话,ES5之前程序将会根据第一个参数自动决定用什么进制。

  1. 第一个字母是x,X:默认16进制

  2. 第一个是0:默认8进制(08、09开头属于无效参数,因为8、9不是八进制数)

  3. 0b:二进制

ES6及以后如果不特意指定第二个参数,都默认10进制。

隐式转换

加号(+)的执行

  1. 非原始类型的调用 ToPrimitive 转换成原始类型

  2. 两边有string的话,执行字符串拼接

  3. 两边都为boolean或者number的话执行加法运算

减号(-)、乘号(*)、除号(/)的执行

会把两边都转换成数字,执行数学运算。非原始类型的调用 ToPrimitive 转换成原始类型。

any -> Boolean 转换时机

  1. if( __ )

  2. for( .. ; __ ; .. )

  3. while( __ )

  4. __ ? .. : ..

  5. __ || __ || __ __ && __ && __

符号的强制类型转换

类型

显式转换

隐式转换

Symbol -> String

✅ Symbol(cool)

Symbol -> Number

Symbol -> Boolean

✅ true

✅ true

符号类型强制转换没有问题,隐式转换会报错

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),再执行比较

使用 == 实现比较执行规则

  1. 有boolean先转换成number

  2. 复杂数据类型和基本类型的先用toPrimitive,两个都是复杂类型就直接比较地址

  3. symbol转换成string

  4. 如果string == number的话,将string转成number再进行比较

“<”和“>”

  1. 首先调用toPrimitive转换成基本数据类型

  2. 如果两侧有number,就尝试把另一边也转成number

  3. 两边都是字符串就从头挨个字符比较顺序

语法

表达式和语句

语句都有一个结果值,在 chrome 的控制台执行语句时展示的值就是,当执行多个语句/执行代码块时,只展示最后一行语句的结果值。

有些语法会屏蔽语句的返回值,例如 var。

某些语句的结果值无法在常规情况下捕捉,例如 if 语句。此时可以利用 eval 方法执行,返回值就是语句的结果值。

ES7 规范有一项“do 表达式”(do expression)提案,能够在这种情况下代替 eval 方法。暂时还没有被采纳。

运算符优先级(左关联)

  1. ()

  2. &&

  3. ||

  4. ... ? ... : ...