JavaScript 奇葩行為大賞
parseInt(0.0000005)
答案:5
parseInt(0.5); // -> 0
parseInt(0.05); // -> 0
parseInt(0.005); // -> 0
parseInt(0.0005); // -> 0
parseInt(0.00005); // -> 0
parseInt(0.000005); // -> 0
parseInt(0.0000005); // -> 5
parseInt 函數(shù)將其第一個(gè)參數(shù)轉(zhuǎn)換為字符串(如果它還不是字符串),然后再轉(zhuǎn)換為數(shù)字。當(dāng)將 0.0000005 轉(zhuǎn)換為字符串時(shí),會(huì)得到以下結(jié)果:
String(0.0000005); // -> "5e-7"
然后 parseInt 函數(shù)只取該字符串的第一個(gè)字符,即 5,并將其解析為一個(gè)數(shù)字。
[] == ![]
答案:true。
[] == ![] 之所以返回 true,是因?yàn)楸容^過程中發(fā)生了隱式的類型轉(zhuǎn)換。下面來逐步解析:
- [] 是一個(gè)空數(shù)組,它是真值。![] 是 false,因?yàn)楫?dāng)將空數(shù)組強(qiáng)制轉(zhuǎn)換為布爾值時(shí),它變?yōu)?nbsp;true,然后被否定為 false。因此,比較變成了 [] == false。
- 當(dāng)比較不同類型時(shí),JavaScript 將嘗試將一個(gè)或兩個(gè)值強(qiáng)制轉(zhuǎn)換為相同類型。在這種情況下,它將嘗試將數(shù)組強(qiáng)制轉(zhuǎn)換為原始值。
- 一個(gè)空數(shù)組,當(dāng)被強(qiáng)制轉(zhuǎn)換為原始值時(shí),變成了一個(gè)空字符串 ""。因此,表達(dá)式 [] == false 實(shí)際上變成了 "" == false。
- 現(xiàn)在,JavaScript 嘗試將布爾值 false 轉(zhuǎn)換為數(shù)字,即 0,表達(dá)式就變成了 "" == 0。
- 根據(jù) JavaScript 的規(guī)則,當(dāng)比較一個(gè)字符串和一個(gè)數(shù)字時(shí),字符串將被強(qiáng)制轉(zhuǎn)換為數(shù)字。因此,"" 被強(qiáng)制轉(zhuǎn)換為數(shù)字后變成了 0。這時(shí)比較的就是 0 == 0,結(jié)果是 true。
NaN === NaN
答案:false。
在 JavaScript 中,NaN(Not a Number)是一個(gè)特殊的值,表示一個(gè)非數(shù)字的值。然而,當(dāng)使用 ===(嚴(yán)格相等運(yùn)算符)來比較 NaN 時(shí),會(huì)出現(xiàn)一個(gè)特殊的情況:NaN 并不等于 NaN。具體來說,NaN === NaN 的結(jié)果是 false,盡管兩者都是 NaN。這是因?yàn)樵?IEEE 754 浮點(diǎn)數(shù)標(biāo)準(zhǔn)中,NaN 被定義為不等于任何其他值,包括它自身。
要檢查一個(gè)值是否是 NaN,通常使用 isNaN() 函數(shù),但請(qǐng)注意,isNaN() 對(duì)于非數(shù)字類型的參數(shù)(如字符串或?qū)ο螅┮部赡芊祷?true,因?yàn)樗鼤?huì)嘗試將這些參數(shù)轉(zhuǎn)換為數(shù)字。更嚴(yán)格的檢查方法是使用 Number.isNaN(),它只有在參數(shù)確實(shí)是 NaN 時(shí)才返回 true。
NaN === NaN // false
isNaN(NaN); // true,但這不是最佳方式
Number.isNaN(NaN); // true,這是更好的方式
[1, 2] + [3, 4]
答案:"1,23,4"。
在 JavaScript 中,當(dāng)嘗試使用 + 運(yùn)算符來連接兩個(gè)數(shù)組,實(shí)際上并不會(huì)執(zhí)行數(shù)組的拼接或合并。相反,由于 + 運(yùn)算符在 JavaScript 中既可以用作加法運(yùn)算符(對(duì)于數(shù)字),也可以用作字符串連接運(yùn)算符(對(duì)于字符串),因此數(shù)組會(huì)首先被轉(zhuǎn)換為字符串,然后再進(jìn)行連接。
數(shù)組到字符串的轉(zhuǎn)換是通過調(diào)用數(shù)組的 toString() 方法實(shí)現(xiàn)的,這通常會(huì)生成一個(gè)由數(shù)組元素組成的逗號(hào)分隔的字符串。因此,[1, 2] 會(huì)被轉(zhuǎn)換為 "1,2",而 [3, 4] 會(huì)被轉(zhuǎn)換為 "3,4"。然后,這兩個(gè)字符串會(huì)被 + 運(yùn)算符連接起來,得到 "1,23,4"。所以,[1, 2] + [3, 4] 的結(jié)果是 "1,23,4"。
如果想要合并兩個(gè)數(shù)組,應(yīng)該使用數(shù)組的 concat() 方法或擴(kuò)展運(yùn)算符如下所示:
- 使用 concat() 方法:
const result = [1, 2].concat([3, 4]); // [1, 2, 3, 4]
- 使用擴(kuò)展運(yùn)算符:
const result = [...[1, 2], ...[3, 4]]; // [1, 2, 3, 4]
typeof null
答案:object。
在 JavaScript 早期版本中,所有值都存儲(chǔ)在 32 位的單元中,每個(gè)單元包含一個(gè)小的 類型標(biāo)簽(1-3 bits) 以及當(dāng)前要存儲(chǔ)的數(shù)據(jù)。類型標(biāo)簽共有五種類型:
000: object - 數(shù)據(jù)類型為 對(duì)象。
1: int - 數(shù)據(jù)類型為 有符號(hào)整數(shù)。
010: double - 數(shù)據(jù)類型為 雙精度的浮點(diǎn)數(shù)。
100: string - 數(shù)據(jù)類型為 字符串。
110: boolean - 數(shù)據(jù)類型為 布爾值。
null 的值是機(jī)器碼 NULL 指針(指針值是000),也就是說null的類型標(biāo)簽也是 000,和object的類型標(biāo)簽一樣,所以會(huì)被判定為object。
try...finally
答案:2
(() => {
try {
return 1;
} finally {
return 2;
}
})();
在JavaScript中,當(dāng)在一個(gè)函數(shù)(包括箭頭函數(shù))的try塊和finally塊中都有return語句時(shí),finally塊中的return語句會(huì)覆蓋try塊中的return語句。這是因?yàn)閒inally塊總是會(huì)被執(zhí)行,無論try塊中的代碼是否成功執(zhí)行,或者是否拋出了異常。而且,如果finally塊中有return語句,那么這個(gè)return語句將決定整個(gè)函數(shù)的返回值。
0.14 * 100
答案:14.000000000000002
0.13 * 100 // 13
0.14 * 100 // 14.000000000000002
0.15 * 100 // 15
0.16 * 100 // 16
在JavaScript中,所有的數(shù)字都是以 64 位浮點(diǎn)數(shù)形式存儲(chǔ)的,即使它們被聲明為整數(shù)。由于二進(jìn)制無法精確表示所有的十進(jìn)制小數(shù),因此在進(jìn)行浮點(diǎn)數(shù)運(yùn)算時(shí),可能會(huì)出現(xiàn)精度問題。由于在二進(jìn)制浮點(diǎn)數(shù)表示中,0.14 不能精確表示,因此在進(jìn)行乘法運(yùn)算時(shí)會(huì)出現(xiàn)微小的舍入誤差。一個(gè)經(jīng)典的問題就是 0.1 + 0.2 不等于 0.3。這兩個(gè)問題出現(xiàn)的原因是一樣的。
0.1 + 0.2 === 0.3 // false
0.1 + 0.5 === 0.6 // true
為了處理這種精度問題,可以使用Number.EPSILON和Math.round、toFixed等方法來比較浮點(diǎn)數(shù)或?qū)⑵涓袷交癁楣潭ㄐ?shù)位數(shù)。如果需要精確計(jì)算,并且不能容忍這種舍入誤差,可以使用特殊的庫,如decimal.js或bignumber.js,它們提供了高精度的十進(jìn)制數(shù)運(yùn)算。
1.toString()
答案:報(bào)錯(cuò)
const num = 1;
num.toString() // 1
1.toString(); // Uncaught SyntaxError: Invalid or unexpected token
1..toString(); // 1
在 JavaScript 中,1.toString() 會(huì)導(dǎo)致一個(gè)語法錯(cuò)誤,因?yàn)辄c(diǎn)號(hào)(.)在這里被解析為浮點(diǎn)數(shù)的一部分,但緊接著并沒有另一個(gè)數(shù)字來形成有效的浮點(diǎn)數(shù)字面量,所以解析器會(huì)拋出一個(gè) Uncaught SyntaxError: Invalid or unexpected token 錯(cuò)誤。
然而,當(dāng)寫 1..toString() 時(shí),情況就不同了。這里有兩個(gè)點(diǎn)號(hào),但第一個(gè)點(diǎn)號(hào)實(shí)際上并不是浮點(diǎn)數(shù)的一部分。這是因?yàn)?JavaScript 的解析器在遇到連續(xù)的點(diǎn)號(hào)時(shí)會(huì)將它們視為一種特殊的語法結(jié)構(gòu),即第一個(gè)點(diǎn)號(hào)被視為數(shù)字 1 的結(jié)尾(盡管在這里它并沒有實(shí)際意義,因?yàn)?1 已經(jīng)是完整的數(shù)字),而第二個(gè)點(diǎn)號(hào)則作為訪問對(duì)象屬性的操作符。
因此,1..toString() 實(shí)際上是這樣被解析的:
- 數(shù)字 1 被解析為一個(gè)完整的數(shù)字字面量。
- 由于緊接著有一個(gè)點(diǎn)號(hào),但它并沒有跟隨另一個(gè)數(shù)字來形成浮點(diǎn)數(shù),所以它被解釋為對(duì)象屬性的訪問操作符。
- 因?yàn)?1 在 JavaScript 中是一個(gè)原始值,它本身并沒有 .toString() 方法,但是在這里,由于點(diǎn)號(hào)操作符的存在,JavaScript 會(huì)嘗試將 1 轉(zhuǎn)換為一個(gè) Number 對(duì)象(這是一個(gè)稱為裝箱或自動(dòng)封裝的過程)。
- 一旦 1 被轉(zhuǎn)換為 Number 對(duì)象,就可以調(diào)用它的 .toString() 方法了。
所以,1..toString() 最終會(huì)返回字符串 "1",盡管這種寫法在實(shí)際編程中并不常見,因?yàn)樗赡軙?huì)引起混淆。更常見的做法是直接對(duì)數(shù)字變量使用 .toString() 方法,也就是上面的第一種寫法。
Math.max() < Math.min()
答案:true。
Math.max() < Math.min() // true
Math.max() // -Infinity
Math.min() // Infinity
在標(biāo)準(zhǔn)的 JavaScript 環(huán)境中,Math.max() 在沒有參數(shù)時(shí)應(yīng)該返回 -Infinity,而 Math.min() 在沒有參數(shù)時(shí)應(yīng)該返回 Infinity。但是,由于 Infinity 總是大于 -Infinity,所以 Math.max() < Math.min() 返回 true。
9007199254740992 === 9007199254740993
答案:trueJavaScript 的 Number 類型是基于 IEEE 754 標(biāo)準(zhǔn) (也稱為 64 位浮點(diǎn)數(shù))實(shí)現(xiàn)的,這意味著它有一些限制,特別是關(guān)于可以精確表示的數(shù)字的范圍和精度。在 IEEE 754 標(biāo)準(zhǔn)中,最大的安全整數(shù)(即可以精確表示的最大整數(shù))是 Number.MAX_SAFE_INTEGER,其值為 9007199254740991(2 的 53 次方減 1)。
當(dāng)嘗試使用大于 Number.MAX_SAFE_INTEGER 的整數(shù)時(shí),JavaScript 會(huì)嘗試將其存儲(chǔ)為一個(gè)近似的浮點(diǎn)數(shù),這可能會(huì)導(dǎo)致精度損失。在這個(gè)例子中,9007199254740992 和 9007199254740993 都會(huì)被轉(zhuǎn)換為近似的浮點(diǎn)數(shù),但由于精度限制,這兩個(gè)數(shù)字可能會(huì)表示為相同的浮點(diǎn)數(shù)值。
因此,如果需要在 JavaScript 中表示大數(shù)字時(shí),建議使用字符串來存儲(chǔ)大數(shù),以避免精度丟失。