Javascript中隐式类型转换的神坑

开篇之前, 大家先思考一个问题, 下面这段代码运行出来之后的结果是多少

1
++[[]][+[]]+[+[]]

首先, 不得不说, 发现并提出这个问题的人是个天才,他怎么会遇到这样的一个问题。

其次,回答这个问题的人更是一个天才,我难以想象他会回答这个问题,更难以想象的是,他的回答是如此的详细和丰富和完整,真正称得上诲人不倦。

不卖关子了, 这道题的答案是10, String类型的, 10

那么, 这个问题怎么解呢? 下面跟着我一起把小车开起来~

回归本源

我们先复习一下: JS中有两种数据类型: 基本数据类型, 复杂数据类型

基本数据类型存储在栈空间中, 类型加上ES6新增的Symbol一共六种

string

number

boolean

undefined

null

symbol

复杂数据类型存储在堆空间中,包含以下三种

function

object

array

但是听说最近爆出了Chrome的61版本存在的安全漏洞的问题

因为V8团队之前做的一个优化,将函数中的内部复杂数据类型直接存储在栈空间了, 导致V8对ES6的解构, for…of,rest参数的解析功能会变慢, 当然, 这又是另外一回事了.

当我们执行加法操作时,JS在想什么

首先还是申明一下,下面的解释是在网上查阅资料, 加上我自己的实践得出的结果, 可能和准确值存在偏差. 欢迎指正

加法运算的隐式转换的执行顺序

加法运算符会触发三种类型转换:

转换为原始值

转换为数字

转换为字符串

通过 ToPrimitive() 将值转换为原始值

如果操作值是个原始值,则直接返回它。

否则,如果操作值是一个对象。则调用 obj.valueOf() 方法。 如果返回值是一个原始值,则返回这个原始值。

否则,调用 obj.toString() 方法。 如果返回值是一个原始值,则返回这个原始值。

否则,抛出 TypeError 异常。

通过 ToNumber() 将值转换为数字

下面的表格解释了 ToNumber() 是如何将原始值转换成数字的

参数 结果
undefined NaN
null +0
boolean true被转换为1,false转换为+0
number 无需转换
string 由字符串解析为数字。例如,”324”被转换为324

通过ToString()将值转换为字符串

下面的表格解释了 ToString() 是如何将原始值转换成字符串的

参数 结果
undefined “undefined”
null “null”
boolean “true” 或者 “false”
number 数字作为字符串。比如,”1.765”
string 无需转换

小小的总结一下

其实说这么多, 最后想表达的还是一个重要思想

对于JS中的加法, 流程是这样的

  • 判断两边的值是否都为普通的String或Number类型, 如果是,则直接进行数学相加或字符串的拼接操作

  • 如果两边并非都为数字或字符串,那么首先会对相加值中的复杂数据类型进行valueOf()操作, 首先确定一下这个复杂数据类型能否通过求值来返回一个简单数据类型, 然后将求值结果与另一个值进行相加.

  • 如果结果不是一个原始值, 则会调用toString()方法,将复杂类型直接转为字符串,进行拼接操作.

码完了, 好累啊, 那么下面我们做一些简单的测试, 对应上面列出的几条语法

1
6 + { valueOf: function () { return 2 } }  // 8

上面的代码中,我们把valueOf()转为了直接返回2, 所以因为在加法运算时会直接运行所以返回了2

没看懂? 没事儿咱们还有一个例子来说明这个问题,再来看一个好玩的

1
2
3
4
5
Number.prototype.valueOf = function() {
return 3
}

new Number(2) == 3 // true

接下来看个简单的

1
[] + [] // ""

为什么返回空值呢? 因为[]的valueOf值就是空值啊

1
[] + {} // ''[object Object]''

同样的,因为{} 的valueOf()返回的是[object Object], 加上前面的, 所以返回了[object Object]

开始的结果解析

看完这些之后,我们再看一下开始的代码

1
++[[]][+[]]+[+[]]

其实这样就很好解释了

根据优先级,会先计算括号内的元素

[[]] 变成了 ['']

[+[]] 变成了 [0]

上面的代码可以简化为

1
++[''][0] + [0]

[''][0] , 数组的第零位,也就是'', 进行自加操作时进行上述的隐式类型转换, 变成了0, 自加1, 所以变成了1

同样的 [0] 会被转换为 0 , 注意是字符类型的 0

所以最后的结果是

1
1 + "0"

所以答案是 ‘10’

(完)

文章目录
  1. 1. 回归本源
  2. 2. 当我们执行加法操作时,JS在想什么
    1. 2.1. 加法运算的隐式转换的执行顺序
    2. 2.2. 通过 ToPrimitive() 将值转换为原始值
    3. 2.3. 通过 ToNumber() 将值转换为数字
    4. 2.4. 通过ToString()将值转换为字符串
  3. 3. 小小的总结一下
  4. 4. 开始的结果解析
|