🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
引言:一個看似簡單的陷阱
在 JavaScript 的日常開發中,我們常常會遇到這樣一段“經典”代碼:
console.log([1, 2, 3].map(parseInt)); // 輸出:[1, NaN, NaN]
乍看之下,這段代碼似乎應該將字符串數組或數字數組轉換為整數數組。然而,結果卻出人意料——除了第一個元素外,其餘全部變成了 NaN。這背後隱藏着 JavaScript 中三個核心概念的交織:Array.prototype.map() 的回調機制、parseInt 的參數行為,以及 NaN 的語義本質。
本文將從這三個維度出發,層層剖析這一常見陷阱,並深入探討它們在實際開發中的正確使用方式,幫助開發者避開“看似合理實則錯誤”的編程誤區。
一、map 方法:不只是遍歷,更是映射
1.1 map 的設計哲學
Array.prototype.map() 是 ES6 引入的重要高階函數之一,其核心思想是函數式編程中的“映射”(mapping) :對原數組的每個元素應用一個函數,並返回一個由結果組成的新數組,而不修改原數組。
const arr = [1, 2, 3]; const squares = arr.map(item => item ** 2); // [1, 4, 9]
這段代碼簡潔而優雅,體現了 map 的典型用途:一對一轉換。
1.2 map 的回調函數簽名
關鍵在於,map 的回調函數實際上接收三個參數:
arr.map((element, index, array) => { ... })
element:當前元素index:當前索引array:原數組本身
雖然我們通常只使用第一個參數,但當我們將一個多參數函數(如 parseInt)直接作為回調傳入時,問題就出現了。
二、parseInt 的隱秘規則:基數決定一切
2.1 parseInt 的正確用法
parseInt(string, radix) 用於將字符串解析為指定基數的整數。其中:
string:要解析的字符串radix(可選):進制基數,範圍 2–36
console.log(parseInt("ff", 16)); // 255(十六進制)
console.log(parseInt("10", 2)); // 2(二進制)
console.log(parseInt("123")); // 123(默認十進制)
當省略 radix 時,JavaScript 會嘗試自動推斷,但這種行為不可靠(例如 "08" 在舊引擎中會被視為八進制)。因此,始終顯式指定 radix=10 是最佳實踐。
2.2 當 map 遇上 parseInt:參數錯位的災難
現在回到那行“陷阱代碼”:
[1, 2, 3].map(parseInt)
等價於:
[1, 2, 3].map((item, index, array) => parseInt(item, index, array))
由於 parseInt 只使用前兩個參數,第三個被忽略。於是實際調用變為:
parseInt("1", 0)→ 基數為 0,按十進制處理 → 1parseInt("2", 1)→ 基數為 1(非法!有效範圍是 2–36)→ NaNparseInt("3", 2)→ 基數為 2(二進制),但 "3" 不是合法二進制數字 → NaN
這就是
[1, NaN, NaN]的真正來源——map 傳遞的索引被誤當作 radix 使用。
2.3 正確的解決方案
要安全地將數組轉為整數,應顯式封裝 parseInt:
// 方案一:箭頭函數限定參數 ["1", "2", "3"].map(str => parseInt(str, 10)); // 方案二:使用 Number 構造函數(更簡潔) ["1", "2", "3"].map(Number); // [1, 2, 3] // 方案三:定義專用函數 const toInt = str => parseInt(str, 10); ["1", "2", "3"].map(toInt);
其中,Number() 更適合純數字字符串轉換,而 parseInt(str, 10) 在處理帶非數字後綴的字符串時更有優勢:
console.log(parseInt("123abc", 10)); // 123
console.log(Number("123abc")); // NaN
三、NaN:JavaScript 中最特殊的“數字”
3.1 NaN 的本質
NaN(Not-a-Number)是 JavaScript 中一個表示無效數值計算結果的特殊值。儘管它的類型是 "number",但它代表的是“無法表示的數字”。
常見產生 NaN 的場景包括:
0 / 0Math.sqrt(-1)"abc" - 10undefined + 10parseInt("Hello")
值得注意的是:
6 / 0返回Infinity(正無窮),而非 NaN-6 / 0返回-Infinity
3.2 NaN 的詭異特性:不等於自己
console.log(NaN === NaN); // false
這是 IEEE 754 浮點標準的規定。因此,不能用相等運算符判斷 NaN。
3.3 正確檢測 NaN 的方法
ES6 引入了 Number.isNaN(),專門用於檢測 NaN:
if (Number.isNaN(parseInt("hello"))) {
console.log("hello 不是一個數字");
}
相比全局的 isNaN(),Number.isNaN() 更安全,因為它不會先進行類型轉換:
isNaN("hello"); // true(先轉為 NaN)
isNaN(undefined); // true(先轉為 NaN)
Number.isNaN("hello"); // false(類型不是 number)
Number.isNaN(NaN); // true
結語:理解機制,方能駕馭語言
[1, 2, 3].map(parseInt) 這個看似微不足道的例子,實則揭示了 JavaScript 設計中的深層邏輯:函數是一等公民,參數傳遞是靈活的,但靈活性也帶來了責任。
只有當我們真正理解:
map如何傳遞參數,parseInt如何解析基數,NaN如何表示無效數值,
才能寫出既簡潔又健壯的代碼。
“在 JavaScript 中,最危險的 bug 往往藏在‘看起來沒問題’的代碼裏。”
—— 而破解它們的鑰匙,正是對語言機制的深刻理解。
掌握這些細節,不僅是技術的提升,更是編程思維的成熟。