inspired by 海绵宝宝meme(
起因
如图所示(其实是因为不熟悉,导致平时用起来有点抗拒

好啦,meme归meme,那么JS这令人诟病的值比较究竟是怎么样的呢?
Type of value
在JS里头,value有两种:primitive和object
primitive分别是 string, number, bigint, boolean, undefined, symbol, null
其特点就是immutable,即它们的值不能被更改。不像数组,对象,函数那样,可以把自身的property给更改。如果一个被赋值了primitive的变量的value被更改,那就是这个变量被赋了个新值。
至于object类型,Objects are held by reference。这跟Java是差不多的。
let a = { x: 114514 };
let b = a; // copy the reference and assign to b
b.x = 1919810;
console.log(a.x); // 1919810
let c = 114514;
let d = c; // assigning a new primitive, but not copying the reference
d = 1919810;
console.log(c); // 114514
undefined指的是一个变量的值为空(没有赋值),这跟null不一样
Symbol是一个有特殊用途的primitive,平时表现为一个隐藏的值。通常作为一个对象的特殊key。
function和array是特殊的Object类型
typeof这里也有一点坑,理论上primitive就显示对应的类型,但是null却不是如此
typeof function() {} // "function"
typeof [1919, 810] // "object" but not "array"
typeof null // "object" but not "null"
Falsy values
这里插入falsy value,为后面比较做铺垫
下面这些值都是false
falsenullundefinedNaN0(zero)-0(Number negative zero)0n(BigInt zero)- empty string ("" or ” or “)
Operator || &&
与此同时,顺便插入跟其他常见语言行为有点不同的&&和||运算符运算规则
| meaning | statement |
|---|---|
| Logic OR | If expr1 can be converted to true, returns expr1; else, returns expr2. |
| Logic AND | If expr1 can be converted to true, returns expr2; else, returns expr1. |
let a = undefined || 114514; // a = 114514
let b = undefined && 1919810; // b = undefined
let c = "you" || "me"; // c = you
let d = "you" && "me"; // d = me
根据短路运算的规则来理解就行了,比如Logic OR,第一个true就扔回第一个,否则就会比较第二个,因此第一个false就会扔回第二个。Logic AND也是如此
并且因为js用来判断false和true的是用falsy value和truthy value,所以使用if语句的时候,要注意哪些东西在js里头是falsy的。
==
==与===这两个在同类型比较的时候,行为是一样的。
但是在两个待比较的值的类型不同的时候,就会产生差别,==会把类型做转换后再进行比较值(强制比较,即Coercive equality)。(注意:这区别于value的单独出现,比如[]是truthy value,但是在做==比较的时候就会强制转换类型)
且优先做primitive numeric comparison,即
- 把
object(如果不是primitive)类型的值转到primitive(使用valueOf()或者toString(),优先前者,没有的话才会考虑调用后者) - 再转为
number(如果此时的primitive还不是数字)进行比较
114514 == "114514" // true, (string -> number)
0 == true // false (boolean -> number)
对于数组的话,用==作比较的时候,会发生类型转换,使用了toString()或valueOf()
[].toString(); // ""
[] == "" // true
[] == "0" // true
[] == 0 // true
[] == false // true
[114514].toString() // "114514"
[114514] == "114514" // true
[114514] == 114514 // true
所以海绵宝宝meme里头的"0" == 0, 0 == []都是true。[].toString()得到"",所以"0" == []是false而'' == []为true
对于null和undefined,spec里面也说了
The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.(8.1) The Null type has exactly one value, called null.(8.2) If x is null and y is undefined, return true.(11.9.3) If x is undefined and y is null, return true.(11.9.3)
因此
null == null // true
undefined == undefined // true
null == undefined // true
undefined == null // true
并且null undefined在与其他值进行比较的时候,都不能进行强制类型转化
5 == null // false
14 == undefined // false
特殊的NaN则是:只要有一边的操作数为NaN那么进行恒等于比较就会是false,不等于的比较则是true
小总结
如果两边都是primitive,那么优先强制转化为number比较
如果两边都是对象的引用,则看这两个引用是不是指向同一个对象
而对于一边是对象而另一边是string或number的情况,那么对象就是转化为primitive
优先使用的是Symbol.toPrimitive。没有的话,就还是toString()和valueOf(),hint为string就是优先toString()再valueOf(),hint为其他就是反之。
const obj = {
[Symbol.toPrimitive](hint) {
if (hint === "default") { // default, number or string
return "114514";
}
}
};
console.log(obj == 114514); // true
> < >= <=
那<和>和>=和<=呢?其比较的原理跟==的一样(没有<==这种东西),所以==的规则还是要懂的
还要注意字符串的比较,是一个一个字符的进行比较(像Java里头String的compareTo()
let x = "10";
let y = "9";
x < y // false
===
===是比较严格的比较。先比较类型,类型相同,再比较值。
但也不是真正的strict equal comparison
NaN === NaN // false
0 === -0 // true
+0 === -0 // true
Object.is
刚刚===出现的这些个问题都可以用Object.is()来解决,所以Object.is()就是====了(手滑
一个polyfill如下(摘自MDN)
if (!Object.is) {
Object.defineProperty(Object, "is", {
value: function (x, y) {
// SameValue algorithm
if (x === y) {
// return true if x and y are not 0, OR
// if x and y are both 0 of the same sign.
// This checks for cases 1 and 2 above.
return x !== 0 || 1 / x === 1 / y;
} else {
// return true if both x AND y evaluate to NaN.
// The only possibility for a variable to not be strictly equal to itself
// is when that variable evaluates to NaN (example: Number.NaN, 0/0, NaN).
// This checks for case 3.
return x !== x && y !== y;
}
}
});
}
最后
知道了一些规矩,感觉也不像之前那么玄学了
虽然被说是糟粕,但是比较大于小于的时候必须要用啊…
所以,玄学来源于未知(强行解释)