上篇文章主要介绍了一下各种数据类型转换成Boolean,Number,String的规则。刻意没有去谈到有关于与运算符相关类型转换。这篇就重点讲一下这方面的知识。
上图
一元运算符 +
一元运算符遵从其他类型转Number规则(ToNumber)。
先判断是不是基本数据类型,如果是
补充Symbol与BigInt
这两种数据类型都不支持隐式类型转换,会报错。
如果是引用数据类型,用ToPtimitive(obj,Number)这个方法。
- 如果对象具有 valueOf 方法,且返回一个原始值,则 JavaScript 将这个原始值转换为数字并返回这个数字
- 否则,如果对象具有
toString
方法,且返回一个原始值,则 JavaScript 将其转换并返回。 - 否则,JavaScript 抛出一个类型错误异常
代码实例
console.log(+1); //1
console.log(+"a"); //NaN
console.log(+true); // 1
console.log(+false); // 0
console.log(+null); // 0
console.log(+undefined); // NaN
console.log(+2n); //error
console.log(+Symbol(1));//error
console.log(+[]); //0
console.log(+[2,3,4]); //NaN
console.log(+{}); //NaN
复制代码
分析一下+[2,3,4]为什么会是NaN.
- 首先数组不是基本数据类型,直接用
ToPtimitive(obj,Number)
这个方法。 - [2,3,4]有valueOf方法但是返回的不是原始数据类型,所以用
toString
,返回”1,2,3″。 - 将”1,2,3″转换为数字发现有非法字符,报错。
二元运算符 +
当计算value1 + value2时,规则如下
- lprim = ToPrimitive(value1)
- rprim = ToPrimitive(value2)
- 如果 lprim 是字符串或者 rprim 是字符串,那么返回 ToString(lprim) 和 ToString(rprim)的拼接结果
- 否则返回 ToNumber(lprim) 和 ToNumber(rprim)的运算结果
文字比较难理解,我们通过代码举几个例子演示一下。
相同数据类型相加
console.log(1 + 1); //2
console.log("a" + "a"); // "aa"
console.log(true + true); //2
console.log(null + null); //0
console.log(undefined + undefined); //NaN
console.log(Symbol(1) + Symbol(2)); //error
console.log(2n + 3n); //5n
console.log(NaN + NaN); //NaN
console.log({} + {}); // [object Object][object Object]
console.log([] + []); //""
复制代码
不同类型相加
true(Boolean) + 其他
console.log(true + "a"); // "truea"
console.log(true + true);// 2
console.log(true + false);// 1
console.log(true + null);// 1
console.log(true + undefined);// NaN
console.log(true + Symbol(1));// error
console.log(true + 2n);// error
console.log(true + []); // "true"
console.log(true + [1, 2, 3]); // "true1,2,3"
console.log(true + {}); // "true[object Object] "
复制代码
“a”(String) + 其他
console.log("a" + "a"); // "aa"
console.log("a" + true); //"atrue"
console.log("a" + false); //"afalse"
console.log("a" + null); // "anull"
console.log("a" + undefined); // "aundefined"
console.log("a" + Symbol(1)); // error
console.log("a" + 2n); // "a2"
console.log("a" + []); // "a"
console.log("a" + [1, 2, 3]); //"a1,2,3 "
console.log("a" + {}); // "a[object Object] "
复制代码
1(Number) +其他
console.log(1 + "a"); // "1a"
console.log(1 + true); // 2
console.log(1 + false); // 1
console.log(1 + null); // 1
console.log(1 + undefined); //NaN
console.log(1 + Symbol(1)); // error
console.log(1 + 2n);//error
console.log(1 + []); // "1"
console.log(1 + [1, 2, 3]); //"11,2,3"
console.log(1 + {}); // "1[object Object]"
复制代码
可以看出,这么不同的类型相加,如果仅仅是凭借记忆力去强行记忆的话,很容易就会忘记。
但是我们利用规则来解决这些问题的话,就显得轻而易举了。
这里我们针对一些比较难以理解的输出分析一下。
相同类型相加中的
console.log(undefined + undefined); //NaN
console.log({} + {}); // [object Object][object Object]
console.log([] + []); //""
复制代码
直接用规则。
undefined + undefined
-
左右两边都属于基本数据类型,所以lprim = undefined, rprim = undefined。
-
两个都不是字符串,所以返回的是ToNumber(lprim) 和 ToNumber(rprim)的运算结果
-
ToNumber(undefined)是什么? 看ToNumber规则发现是NaN
-
NaN + NaN 最终的结果就是NaN
{} + {}
-
{}
属于引用数据类型,所以用ToPrimitive(obj,Number)。 -
先使用
{}
的valueOf方法,发现返回的不是原始数据类型,于是用toString方法,返回"[object object]"
,字符串类型。 -
左右两边都是字符串类型,返回 ToString(lprim) 和 ToString(rprim)的拼接结果。
-
"[object object]"
+"[object object]"
最终的结果就是"[object object][object object]"
[] + []
这道美味就留给读者老爷们啦。
另外其他的有关于二元运算符+的类型转换,读者老爷们如果有时间可以试着去做一下。有问题的请在文章下方留言,我会问您解答的,如果我解答不了,我会想办法帮您解答的。
还有一点值得注意的是Symbol类型不能用来运算,从上面的结果也可以看出。BigInt也只能和它自己本身运算。
==相等
说实话,这玩意用于类型转换的规则实在太多了,而且也不是很规范,所以我建议大家还是去使用更加标准以及严格的===运算符。
不过考虑到部分读者可能会优点强迫症,这里也贴一下具体的规则。
"=="
用于比较两个值是否相等,当要比较的两个值类型不一样的时候,就会发生类型的转换。
关于使用”==”进行比较的时候,具体步骤可以查看规范11.9.5:
当执行x == y 时:
两边的类型是否相同,相同的话就比较值的大小,例如1==2,返回false
判断的是否是null和undefined,是的话就返回true
判断的类型是否是String和Number,是的话,把String类型转换成Number,再进行比较
判断其中一方是否是Boolean,是的话就把Boolean转换成Number,再进行比较
如果其中一方为Object,且另一方为String、Number或者Symbol,会将Object转换成字符串,再进行比较
妈呀,太多了。
===相等
===判断是否相等相对于==来说简化了太多,具体来说就一条。
左右两边类型与值是否都完全一致,是就返回true,否则返回false。
但是===存在两个bug,这两个bug其实也存在与==中
- === 认为NaN不等于自身,,但是这并不符合人类正常逻辑思维。
- 还有就是 === 认为+0 与 -0 是完全相同的。
console.log(NaN === NaN) // false
console.log(+0 === -0) // true
复制代码
于是在ES6中,Object新增一个is方法修复了这两个bug。
Object.is()的作用与===类型,也是判断两个变量是否相等,但是它修复了NaN不等于自身以及+0等于-0这两个不符合人类正常逻辑思维的bug。
Object.is(), === 和 == 区别
- 两等号判等,会在比较时进行类型转换。
- 三等号判等(判断严格),比较时不进行隐式类型转换,(类型不同则会返回false)
- 使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 +0 不再相等,两个 NaN 认定为是相等的。
结语
感谢您看到这里