博客 / 詳情

返回

JavaScript 中的 map、parseInt 與 NaN:一場關於類型轉換與函數調用的深度解析

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

引言:一個看似簡單的陷阱

在 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,按十進制處理 → 1
  • parseInt("2", 1) → 基數為 1(非法!有效範圍是 2–36)→ NaN
  • parseInt("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 / 0
  • Math.sqrt(-1)
  • "abc" - 10
  • undefined + 10
  • parseInt("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 往往藏在‘看起來沒問題’的代碼裏。”
—— 而破解它們的鑰匙,正是對語言機制的深刻理解。

掌握這些細節,不僅是技術的提升,更是編程思維的成熟。

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

user avatar jingdongkeji 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.