通过重载,+ 运算符即能用于数字加法,也能用于字符串拼接。
如果 + 的其中一个操作数是字符串(或者通过以上步骤可以得到字符串), 则执行字符串拼接;否则执行数字加法。
我们可以将数字和空字符串 “” 相 + 来将其转换为字符串:
var a = 42;
var b = a + "";
b; // "42"
var a = "42";
var b = "0";
var c = 42;
var d = 0;
a + b; // "420"
c + d; // 42
var a = [1,2];
var b = [3,4];
a + b; // "1,23,4"
因为数组的 valueOf() 操作无法得到简单基本类型值,于是它转而调用 toString()。因此上例中的两 个数组变成了 “1,2” 和 “3,4”。+ 将它们拼接后返回 “1,23,4”。
a + “”(隐式)和前面的String(a)(显式)之间有一个细微的差别需要注意。
a + “” 会对 a 调用 valueOf() 方法,然后通过 ToString 抽象 操作将返回值转换为字符串。而 String(a) 则是直接调用 ToString()。
var a = {
valueOf: function() { return 42; },
toString: function() { return 4; }
};
a + ""; // "42"
String( a ); // "4"
下面的情况会发生 布尔值隐式强制类型转换。
&& 和 || 运算符的返回值并不一定是布尔类型,而是两个操作数其中一个的值
var a = 42;
var b = "abc";
var c = null;
a || b; // 42
a && b; // "abc"
c || b; // "abc"
c && b; // null
换一个角度来讲
a || b; // 大致相当于(roughly equivalent to):
a ? a : b;
a && b; // 大致相当于(roughly equivalent to):
a ? b : a;
既然返回的不是 true 和 false,为什么 a && (b || c) 这样的表达式在 if 和 for 中没出过问题
var a = 42;
var b = null;
var c = "foo";
if (a && (b || c)) {
console.log( "yep" );
}
这里 a && (b || c) 的结果实际上是 “foo” 而非 true,然后再由 if 将 foo 强制类型转换为 布尔值,所以最后结果为 true。
var s1 = Symbol( "cool" ); String( s1 ); // "Symbol(cool)"
var s2 = Symbol( "not cool" ); s2 + ""; // TypeError
var a = 42;
var b = "42";
a === b; // false
a == b; // true
具体怎么转换?是 a 从 42 转换为字符串,还是 b 从 “42” 转换为数字?
ES5 规范 11.9.3.4-5 这样定义:
(1) 如果 Type(x) 是数字,Type(y) 是字符串,则返回 xToNumber(y) 的结果。
(2) 如果 Type(x) 是字符串,Type(y) 是数字,则返回 ToNumber(x)y 的结果。
var a = "42";
var b = true;
a == b; // false
规范 11.9.3.6-7 是这样说的:
(1) 如果 Type(x) 是布尔类型,则返回 ToNumber(x)y 的结果;
(2) 如果 Type(y) 是布尔类型,则返回 xToNumber(y) 的结果。
var x = true;
var y = "42";
x == y; // false
Type(x) 是布尔值,所以 ToNumber(x) 将 true 强制类型转换为 1,变成 1==“42”,二者的 类型仍然不同,“42” 根据规则被强制类型转换为 42,最后变成 1==42,结果为 false。
反过来也一样:
var x = "42";
var y = false;
x == y; // false
Type(y) 是布尔值,所以 ToNumber(y) 将 false 强制类型转换为 0,然后 "42"==0
再变成42==0
,结果为 false。
也就是说,字符串 “42” 既不等于 true,也不等于 false。一个值怎么可以既非真值也非假 值,这也太奇怪了吧?
这个问题本身就是错误的,我们被自己的大脑欺骗了。
“42” 是一个真值没错,但 "42"中并没有发生布尔值的比较和强制类型转换。这里 不是 “42” 转换为布尔值(true),而是 true 转换为 1,“42” 转换为 42。这里并不涉及 ToBoolean,所以 “42” 是真值还是假值’=='本身没有关系!
null 和 undefined 之间的(==)也涉及隐式强制类型转换。
ES5 规范 11.9.3.2-3 规定:
(1) 如果 x 为 null,y 为 undefined,则结果为 true。
(2) 如果 x 为 undefined,y 为 null,则结果为 true
在双等号中 null 和 undefined 相等(它们也与其自身相等),除此之外其他值都不存在这种 情况。
关于对象(对象 / 函数 / 数组)和标量基本类型(字符串 / 数字 / 布尔值)之间的相等比 较,
ES5 规范 11.9.3.8-9 做如下规定:
(1) 如果 Type(x) 是字符串或数字,Type(y) 是对象,则返回 x== ToPrimitive(y) 的结果;
(2) 如果 Type(x) 是对象,Type(y) 是字符串或数字,则返回 ToPromitive(x)==y 的结果。
== 这里只提到了字符串和数字,没有布尔值,因为布尔值会先被强制类型转换为数字。==
下面分别列出了常规和非常规的情况:
"0"==null; // false
"0" == undefined; // false
"0" == false; // true -- 晕!
"0" == NaN; // false
"0" == 0; // true
"0" == ""; // false
false == null; // false
false == undefined; // false
false == NaN; // false
false == 0; // true -- 晕!
false == ""; // true -- 晕!
false == []; // true -- 晕!
false == {}; // false
"" == null; // false
"" == undefined; // false
"" == NaN; // false
"" == 0; // true -- 晕!
"" == []; // true -- 晕!
"" == {}; // false
0 == null; // false
0 == undefined; // false
0 == NaN; // false
0 == []; // true -- 晕!
0 == {}; // false
[] == ![] // true
根据 ToBoolean 规则,它会进行布尔 值的显式强制类型转换(同时反转奇偶校验位)。所以 [] == ![]
变成了[] == false
。前 面我们讲过 false == []
,最后的结果就顺理成章了。
2 == [2]; // true
"" == [null]; // true
介绍 ToNumber 时我们讲过,数组的 valueOf() 返回数组本身, 所以强制类型转换过程中数组会进行字符串化
第一行中的 [2] 会转换为 “2”,然后通过 ToNumber 转换为 2。
第二行中的 [null] 会直接转 换为 “”。
0 == "\n"; // true
“”、"\n"(或者 " " 等其他空格组合)等空字符串被 ToNumber 强制类型转换 为 0
42 == "43"; // false
"foo" == 42; // false
"true" == true; // false
42 == "42"; // true
"foo" == [ "foo" ]; // true
总结:
图片来源地址:https://dorey.github.io/JavaScript-Equality-Table/(需要)