理解 JavaScript 中的梗
- typeof NaN === “number”
 
NaN 同 Number.NaN 一样,都是表示 Not A Number。从 Number.NaN 可以推测 NaN 是数字类型。
但实际上 NaN 不是 JavaScript 创造的。IEEE 754 中将 NaN 定义为一种“特殊”的数字。
- 9999999999999999 === 10000000000000000
 
JavaScript 中所有算数都是 IEEE 754 定义的双精度浮点数(链接),其可以表示的最大整数和浮点数分别在 JavaScript 中定义为 Number.MAX_SAFE_INTEGER(9007199254740991, 2^53-1) 和 Number.MAX_VALUE。
超出 9007199254740991 后,双精度浮点数只能表示偶数了,而且在不同范围内,步进还不相同。例如在 2^53 到 2^54,步进为 2。
1  | > Number.MAX_SAFE_INTEGER  | 
所以双精度浮点数无法表示 9999999999999999,只能将 9999999999999999 近似为 10000000000000000。
- 0.1 + 0.2 != 0.3
 
同样是浮点数的问题。双精度浮点数除了不能准确表示 9999999999999999 之类的整数,也不能准备表示 0.1 之类的小数。
小数的加减法,往往都是“随缘”算法。将 0.1 到 0.5 的几个小数打印为二进制:
1  | > (0.1).toString(2)  | 
除了 0.5,其余几个在二进制下都是无限不循环小数,在浮点数表示法中都会损失精度。
- Math.max() === -Infinity
 
这个其实不算是坑,如果是 Number.MAX_VALUE === -Infinity 那才算坑。可以将 Math.max 理解为有一个隐参数 -Infinity,决定了返回值的下限。
- 加号问题
 
JavaScript 中加号有三个用途,一元运算符、二元运算符的算数加法和字符串拼接。无论是什么用途,都要求运算的值为原始类型,或可被转换成原始类型的对象。
依据提示,对象可转化为 string 或 number。提示分为 string 和 number 和 default,其中 default 会以 number 进行处理。在两元运算中,运算值既可以是 string 也可以是 number,所以转换的提示为 default。具体的转换还依赖于对象的 toString 和 valueOf 方法。详细的算法可以可以在这里找到。
一些例子:
1  | > ({ valueOf() { return 1 }}) + 1  | 
所以 []+[] => ""+"" => "";[]+{} => "" + "[object Object]" => "[object Object]"。
当加号的左右的运算值都为基本类型,并且其中一个为 string,则会应用字符串拼接算法,否则应用算数加法。运算过程中还可能发生基本类型之间的隐式转换。
所以 true+true+true => 1+1+1 => 3;!+[]+[]+![] => (!+[])+[]+(![]) => (!0)+[]+(![]) => true+""+false => "truefalse"。
这里比较费解的是,为何 {}+[] 等于 0?原因是这里的 {} 会识别为空语句块,从而 + 用作一元运算符,其含义是将运算值转换为 number。所以 {}+[] => +[] => +"" => 0。
{} 识别为空对象还是空语句块,有一个大体的规则:若语句(statement)以 { 开头,则识别为语句块,否则识别为对象。当然生成中应当避免这类有歧义的写法,况且{}在各个 JavaScript 引擎中也有些差异,例如 {}+{} 在 Firefox 下可能输出 NaN,而在 Safari 下可能输出为 "[object Object][object Object]"
- 相等问题
 
JavaScript 提供了 == 和 === 两个比较运算符。前者会将运算值转化为相同类型再应用 ===,后者等价于 C Java 等编程语言中的 ==。
所以 == 可以进行对象和原始类型的比较:
1  | > 1 == { valueOf() { return 1 } }  | 
值得一提的是,JavaScript 中还存在其他的比较算法 SameValue 和 SameValueZero。前者可通过 Object.is 来直接使用,后者可以通过 Array.prototype.includes 的方法来间接使用。
具体的例子如下:
1  | > NaN == NaN  |