先来一波运算符的优先级 :

javaScript运算符优先级

原始类型之间的隐式转换

js 有五种原始值

1
["Null", "Undefined", "String", "Boolean", "Number"];
原始类型的值 转换为数值 转换为字符串 转换为布尔
false 0 “false” false
true 1 “true” true
0 0 “0” false
1 1 “1” true
“0” 0 “0” false
“1” 1 “1” true
NaN NaN “NaN” false
infinity infinity “infinity” true
-infinity -infinity “-infinity” true
“” 0 “” true
“name” NaN “name” true
null 0 “null” false
undefined NaN “undefined” false
引用类型的值 转换为数值 转换为字符串 转换为布尔
[] 0 “” true
[10] 10 “10” true
[10,20] NaN “10,20” true
{} NaN “[Object Object]” true

“+” 号的运算规则

js 在加法运算的时候会把运算对象转换为原始值然后进行运算

1. 使用ToPrimitive转换为原始值

JS 引擎内部转换为原始值时,都会调用ToPrimitive函数转换,然后在ToPrimitive函数内部会调用valueOf()toString()方法进行转换

转换规则如下:

如果运算值不是 Date 类型,那么在运算转换的时候

  1. 如果 obj 为原始值,直接返回;
  2. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
  3. 否则调用 obj.toString(),如果执行结果是原始值,返回之;
  4. 否则抛异常。

如果是 Date 类型的话,将上面的第 2 步和第 3 步调换,即:

  1. 如果 obj 为原始值,直接返回;
  2. 否则调用 obj.toString(),如果执行结果是原始值,返回之;
  3. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
  4. 否则抛异常。

toString 用来返回对象的字符串表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {
name: "obj"
};
console.log(obj.toString()); // "[Object Object]"

var obj1 = {};
console.log(obj1.toString()); // "[Object Object]"

var arr = [10];
console.log(arr.toString()); // "10"

var arr1 = [];
console.log(arr1.toString()); // ""

var date = new Date();
console.log(date.toString()); // Wed Apr 11 2018 11:52:55 GMT+0800 (中国标准时间)

valueOf 用来返回对象的原始值,可能是字符串、数值或 bool 值等,看具体的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {
name: "obj"
};
console.log(obj.valueOf()); // {name: "obj"}

var obj1 = {};
console.log(obj1.valueOf()); // {}

var arr = [10];
console.log(arr.valueOf()); // [10]

var arr1 = [];
console.log(arr1.valueOf()); // []

var date = new Date();
console.log(date.valueOf()); // 1523419204170

2. 原始值之间的隐式转换

  • 如果在一端出现字符串类型的值时,转换为字符串进行运算
  • 其他情况转换为数值进行运算

所以可以将上面的代码以加号分割成左右俩部分

左: ++[[]][+[]]

右: [+[]]

温馨提示:

1
2
3
4
[] :
转换成字符串会转换为""
转换为数字会转换为0
转换为布尔值会转换为true

注意点 几种强制转换类型

1
2
3
+[] 前面有加号会把运算对象转换为数字
+new Date 转换成时间戳
! 会把运算对象转换为bool值

左边细分: ++[[]] 和 [+[]]

  1. ([]优先级高于 ++ 先算[]里的)
  2. +[] 前面有+会转换为数字 得 [0]
  3. 左边最终为 ++[[]][0] 取出数组[[]]的第一个元素累加即:++[]
  4. ++[] 进行累计运算 [] 转换为 数值 0++0;

右边细分:+[] 转换为数字 得 0

  1. 右边最终为 [0]
  2. 最终的表达式就成了 ++0+[0]
  3. 运算的时候,对[0]使用 valueOf()方法,返回的不是原始值,继续调用 toString 方法得到"0",然后在进行 ++0+"0" , ++ 优先于 + 最终结果得到的就是 1+ "0" ="10";

总结

以上我们可以得出一个结论:
js 在进行加法运算的时候

  1. 首先会对加号俩边不是原始值的数据进行ToPrimitive转换为原始值
  2. 在俩边都是原始值后,会进行原始值之间适当的隐式转换,再进行运算

相等比较的隐式转换规则

  1. 只要布尔类型参与比较,该布尔类型的值首先会被转换为数字类型
  2. 当数字类型和字符串类型做相等比较时,字符串类型会被转换为数字类型
  3. 当对象类型和原始类型做相等比较时,对象类型会依照 ToPrimitive 规则转换为原始类型
  4. nullundefined 之间互相宽松相等(==),并且也与其自身相等,但和其他所有的值都不宽松相等(==)。
  • 推荐 github 上一个 repo jsfuck 各种隐式转换的情况

参考链接

jawil’s blog