JS Falsy 假值
我见的太多了,JS 代码里面 Js-Lint 警告被置之不理。最多的估计是花括号和分号的缺失了,另外有很大一部分是滥用 ==
来做以下比较:
x == false
x == null
x == undefined
x == 0
x == ""
x == 1
x == true
其实这些比较实在很没必要,关键是还很不安全,往往这会给维护的人(哪怕是原作者本人)带来疑惑:究竟你的意图是真的要和这些值做比较呢,还是只要真或假就可以了?更糟糕的是,这样的比较往往会产生意想不到的后果。
我用以下代码产生了几张表格:
// js
var falsyArr = [false, 0, NaN, null, undefined, "", "0", " trn "],
ifArr = [],
compareArr = [],
i, j,
getFalsyDisplay = function(falsy) {
switch (falsy) {
case null:
return "null";
case "":
return "\"\"";
case "0":
return "\"0\"";
case " trn ":
return "\" \t\r\n \"";
default:
return falsy;
}
};
for (i = 0; i < falsyArr.length; i++) {
var iArr = [];
for (j = 0; j < falsyArr.length; j++) {
iArr.push({
"==": falsyArr[i] == falsyArr[j],
"!=": falsyArr[i] != falsyArr[j],
"===": falsyArr[i] === falsyArr[j],
"!==": falsyArr[i] !== falsyArr[j]
});
}
ifArr.push(falsyArr[i] ? true : false);
compareArr.push(iArr);
}
var createTbl = function(comp) {
var id, tbl, row, cell;
switch (comp) {
case "==":
id = "eqeq";
break;
case "!=":
id = "noteq";
break;
case "===":
id = "eqeqeq";
break;
case "!==":
id = "noteqeq";
break;
default:// if
id = "if";
break;
}
tbl = document.getElementById(id);
if (tbl) {
tbl.parentNode.removeChild(tbl);
}
tbl = document.createElement("table");
tbl.id = id;
row = tbl.insertRow(0);
cell = document.createElement("th");
cell.innerHTML = getFalsyDisplay(comp || id);
cell.style.color = "#F30";
row.appendChild(cell);
for (i = 0; i < falsyArr.length; i++) {
cell = document.createElement("th");
cell.innerHTML = getFalsyDisplay(falsyArr[i]);
row.appendChild(cell);
}
document.body.appendChild(tbl);
return tbl;
}, createCompareTbl = function(comp) {
var tbl = createTbl(comp), row, cell;
for (i = 0; i < falsyArr.length; i++) {
row = tbl.insertRow(i + 1);
cell = document.createElement("th");
cell.innerHTML = getFalsyDisplay(falsyArr[i]);
row.appendChild(cell);
for (j = 0; j < falsyArr.length; j++) {
cell = row.insertCell(j + 1);
if (i <= j) {
cell.innerHTML = compareArr[i][j][comp] ? "Y" : "N";
cell.className = compareArr[i][j][comp];
}
}
}
}, createIfTbl = function() {
var tbl = createTbl(),
row = tbl.insertRow(1),
cell = document.createElement("th");
row.appendChild(cell);
for (i = 0; i < falsyArr.length; i++) {
cell = row.insertCell(i + 1);
cell.innerHTML = ifArr[i] ? "Y" : "N";
cell.className = ifArr[i];
}
};
createIfTbl();
createCompareTbl("==");
createCompareTbl("===");
createCompareTbl("!=");
createCompareTbl("!==");
/* css */
body {
padding: 0;
margin: 0;
}
table#eqeq,
table#noteq,
table#eqeqeq,
table#noteqeq,
table#if {
table-layout: fixed;
width: 630px;
}
table#eqeq th,
table#eqeq td,
table#noteq th,
table#noteq td,
table#eqeqeq th,
table#eqeqeq td,
table#noteqeq th,
table#noteqeq td,
table#if th,
table#if td {
text-align: center;
font-size: 9pt;
font-family: Arial;
}
table#eqeq th,
table#noteq th,
table#eqeqeq th,
table#noteqeq th,
table#if th {
background-color: #DDD;
}
table#eqeq td,
table#noteq td,
table#eqeqeq td,
table#noteqeq td,
table#if td {
background-color: #EEE;
}
table#eqeq td.true,
table#noteq td.true,
table#eqeqeq td.true,
table#noteqeq td.true,
table#if td.true {
background-color: #CFC;
}
table#eqeq td.false,
table#noteq td.false,
table#eqeqeq td.false,
table#noteqeq td.false,
table#if td.false {
background-color: #FCC;
}
首先最奇怪的是 NaN
,众所周知了,它与任何东西都不相等,甚至自己都不等于自己。
然后奇怪的是 null
和 undefined
,它们是假值,互相之间可以 ==
,但不等于 false
,更不等于 true
。
然后是 "0"
和 " \t\r\n "
这两个亦真亦假的值,if
判断的时候 "0"
和 " \t\r\n "
都为真,但是它们却 == false
,再试一下 "0" == true
和 " \t\r\n " == true
,结果返回的是 false
…悖论 - 以假乱真?
假的东西在 if
的时候是真的,事实上它们是应该为真,但是再跟 true / false
做比较的时候,万恶的解释器对他们做了类型转换:
- 先把
String
先trim
- 然后
- 空串则
== false
- 非空串,转换成数字,为
0
则== false
- 空串则
所以,就有了如下的奇葩结果:
"0" == false
" 0" == false
\t\r\n00000000000 == false
在数学里,我们有 A == B, B == C ⇒ A == C
,数学是严谨的,但 JS 不是,在这里,你很有可能得出这样的结论:A == B, B == C ⇒ A != C
,比如:"0" == 0, 0 == "" ⇒ "0" != ""
。这也是类型转换造成的,"0" == 0
会把后面的 0
toString()
转成 String
型,0 == ""
则会把两个都转成 Boolean
型,而 "0" == ""
因为两个都是 String 型所以无需转换,就不等了。
其实要避免这种错误的类型转换很简单,一个就是尽量用 ===
和 !==
,另外就是用 if (!x)
代替 if (x == false)
,if (x)
代替 if (x == true)
之类的语句。
不要无所谓,尽量让你的 JS 变成 Js-Lint Warn-Free 吧,对你对项目都是有好处的。