likes
comments
collection
share

🚀隐藏在 1 + '1' = '11' 背后的真相

作者站长头像
站长
· 阅读数 20

前言

如果问你 1 + '1' = ?,想必你会毫不犹豫脱口而出,答案等于 '11',自信的背后源于这句话:“字符串加任何值都等于字符串”。可是你有没有想过,为什么这个等式得到的是'11'(数字1融入到字符串'1'中),而不是2(字符串'1'融入数字1中)呢?

其实,这和下文要讲的二元操作符有关,可见这些时时刻刻进行着的类型转换,组成了我们代码高速运转的核心。

类型转换

类型转换分为显示类型转换隐式类型转换,方法也主要分为Number()String()Boolean()三种,以及与类型转换涉及极深的操作符们。

Number()

这是es5官方文档中对Number()的解释:

🚀隐藏在 1 + '1' = '11' 背后的真相

意思是如果提供了值,则返回由 ToNumber(value) 计算的 Number 值(不是 Number 对象),否则返回 0。

也就是说调用Number会执行ToNumber方法,那么再让我们来看看ToNumber的执行过程:

🚀隐藏在 1 + '1' = '11' 背后的真相

简单来说就是当传进来以下类型时:

  1. Undefined:返回NaN
  2. Null:返回 0。
  3. Boolean:如果参数为 true,则返回 1。如果参数为 false,则返回 0。
  4. Number:返回结果等于输入参数(无转换)。
  5. String:总结来说就是,只要有非数字就返回NAN,其他就返回数字(空字符串返回的是0)
  6. Object:会调用ToPrimitive方法,返回ToPrimitive得到的返回值。

String()

同样的,我们来看看es5官方文档中对String()的解释:

🚀隐藏在 1 + '1' = '11' 背后的真相

这段话说的是返回由 ToString(value) 计算的 String 值(不是 String 对象)。如果未提供 value,则返回空字符串""  。

同样,调用String会执行ToString方法,以下为传入不同类型参数时ToString的执行过程:

🚀隐藏在 1 + '1' = '11' 背后的真相
  1. Undefined:返回Undefined
  2. Null:返回Null
  3. Boolean:如果参数为 true,则返回 "true" ,如果参数为 false,则返回  "false"  。
  4. Number:总结来说就是将传入的数字以字符串形式返回,例如 1 返回为 String"0" ,NaN返回为String"NaN" 。
  5. String:返回输入参数(无转换)。
  6. Object:会调用ToPrimitive方法,返回ToPrimitive得到的返回值。

Boolean()

在这三种类型转换中,Boolean()类型转换可谓是其中最简单的一种,它会调用ToBoolean方法:

🚀隐藏在 1 + '1' = '11' 背后的真相

总结来说就是传入类型为UndefinedNull时,返回都为false。对于Number数,如果参数为 +0、−0 或 NaN,则结果为 false,否则,结果为 true。对于String,如果参数为空字符串(其长度为零),则结果为 false,否则,结果为 true,而区别于上两种方法的是,对于Object,只要传入的为对象,返回都为true

好了,现在你会发现,以上大多都为原始类型之间的转化,而当对象(Object)转化为数字(Number)与字符串(String)时,都会用到一个名为ToPrimitive的方法,它正是类型转换中至关重要的部分

ToPrimitive的执行过程

ToPrimitive,当我们想把对象(Object)转化为数字(Number)和字符串(String)时,ToNumber()ToString()会调用的方法。也就是说ToPrimitive这个函数的功能就是把引用类型转变成原始类型

ToPrimitive方法有两个参数,一个是argument(类型转换的参数),另一个是type(要转换的类型),它的执行过程如下

第一种情况:ToPrimitive(obj, String) ==> String(obj)

  1. 如果接收到的是原始值,直接返回值。
  2. 否则,调用toString 方法,如果得到原始值,返回。
  3. 否则,调用valueOf 方法,如果得到原始值,返回。
  4. 经过上两步都没得到原始值的话,报错。

第二种情况:ToPrimitive(obj, Number) ==> Number(obj)

  1. 如果接收到的是原始值,直接返回值。
  2. 否则,调用valueOf方法,如果得到原始值,返回。
  3. 否则,调用toString 方法,如果得到原始值,返回。
  4. 经过上两步都没得到原始值的话,报错。

可以看出将对象分别转化为字符串数字的过程仅有2、3两步的顺序调换。

toString 与 valueOf

所有对象转原始值都会调用 toString 方法, toString方法共有三种:

  1. {}.toString() 返回由"[object 和 class 和 ]“ 组成的字符串
  2. [].toString() 返回由数组内部元素以逗号拼接的字符串
  3. xx.toString() 返回字符串字面量

valueOf 也可以将对象转化为原始类型,但仅限于包装类对象。

🚀隐藏在 1 + '1' = '11' 背后的真相

操作符

一元操作符

一元操作符表现为+ xxx,将+添加在需要类型转换值的前面,与Number()执行原理相同。如以下代码:

Number(xxx)  ==  + xxx

console.log(Number(undefined));  // 返回 NaN
console.log(Number(null));       // 返回 0
console.log(+ undefined);        // 返回 NaN
console.log(+ null);             // 返回 0

二元操作符

v1 + v2 的执行过程如下:

1. lprim = ToPrimitive(v1)
2. rprim = ToPrimitive(v2)
3. 如果 lprim 或者 rprim 是字符串,那么就 ToString(lprim) 或者 ToString(rprim) 再拼接。
4. 否则 ToNumber(lprim) + ToNumber(rprim)

下文的揭秘 1 + '1' = '11' 就是个很好的二元操作符例子。

==

我把官网中关于 == 的判断结果整合为以下表格:

xy判断结果
UndefinedUndefinedtrue
NullNulltrue
NumberNaNfalse
+0-0true
-0+0true
StringStringtrue/false (根据内容是否完全相同)
BooleanBooleantrue/false (如果为true或false)
引用同一对象引用同一对象true
nullundefinedtrue
undefinednulltrue
NumberStringx == ToNumber(y)
StringNumberToNumber(x) == y
Boolean/ToNumber(x) == y
/Booleanx == ToNumber(y)
String/NumberObjectx == ToPrimitive(y)
ObjectString/NumberToPrimitive(x) == y

同时,在此附上一张Javascript真值表,所有的类型转换都包含在这张矩阵���内。

🚀隐藏在 1 + '1' = '11' 背后的真相

解答:1 + '1' 为啥 = '11' ?

好了,现在类型转换这块没有人比你更熟了,得出其过程自然也毫不费力。在二元操作符的作用下, 1 + '1' 这个过程被执行为:

v1 + v2(1 + '1'):

1. lprim = ToPrimitive(1)     // 为原始类型,返回  1
2. rprim = ToPrimitive('1')   // 为原始类型,返回 '1'

3. ToString(1) + '1'          //在 lprim 与 rprim 中 rprim 是字符串,于是调用 ToString(1) 再拼接。

4. 1 + '1' = '11'             //返回结果等于'11'

这个过程正是“字符串加任何值都等于字符串”的本质。

最后

如果这篇文章能让你对类型转换的理解稍微多一点的话,那它的存在便有了意义。

🚀隐藏在 1 + '1' = '11' 背后的真相
转载自:https://juejin.cn/post/7388470580464812084
评论
请登录