奇怪的JavaScript:map和parseInt的反常應用

2021-02-19 前端之巔
在 JavaScript 中,當使用 map 和 parseInt 將字符數組轉換為整數時,卻出現了奇怪的現象。控制臺輸入 ['1', '7', '11'].map(parseInt)後,得到的結果是 [1, NaN, 3] ,而不是 [1, 7, 11] 。本文將通過解釋一些相關的 JavaScript 概念,解答出現這種狀況的原因。

JavaScript 很奇怪。不相信嗎?嘗試使用 map 和 parseInt 將字符數組轉換為整數。打開控制臺(Chrome 上的快捷鍵是 F12),粘貼下面的代碼,然後按回車鍵(或將焦點放到下面)。我們得到的不是一個整數數組 [1, 7, 11] ,而是 [1, NaN, 3] 。為什麼呢?

['1', '7', '11'].map(parseInt);

要想了解到底發生了什麼,我們首先要討論一些 JavaScript 的概念。如果你想要一個 TLDR(Too Long; Didn’t Read:太長了,不想讀),我在本文的末尾提供了一個快速總結。

下面是 JavaScript 中的一個簡單 if-else 語句:

if (true) {
    
} else {
    
}

在本例中,if-else 語句的條件為 true,因此它總會執行 if 塊,而忽略 else 塊。這是一個很簡單的例子,因為 true 是一個布爾值。如果我們把一個非布爾值作為條件呢?

if ("hello world") {
    
    console.log("Condition is truthy");
} else {
    
    console.log("Condition is falsy");
}

嘗試在開發人員的控制臺(在 Chrome 上按 F12)中運行此代碼。我們會發現 if 塊運行了。這是因為字符串對象「hello world」是真。

每個 JavaScript 對象要麼是真,要麼是假。當將它們放到布爾上下文中時,如放在 if-else 語句中,對象將根據其真實性被視為 true 或 false。那麼哪些對象是真,哪些是假呢?這裡有一個簡單的規則:

除了 false、0、「」(空字符串)、null、undefined、及 NaN 以外, 所有值都是真。

令人困惑的是,這意味著字符串 「false」 、字符串 「0」 、空對象 {} 和空數組 [] 都是真。我們可以通過將一個對象傳遞到布爾函數中(例如 Boolean("0"); 中),來對其進行雙重檢查。

就我們的目的而言,只要記住 0 是假就足夠了。

    基數    

當我們從 0 數到 9 時,每個數字(0-9)都有不同的符號。然而,一旦達到 10,我們需要兩個不同的符號(1 和 0)來表示這個數字。這是因為我們的十進位計數系統的基數(或基礎)是 10。

基數是一個最小的數字,它只能由多個符號來表示。不同的計數系統有不同的基數,因此,相同的數字在不同的計數系統中,可以表示不同的數字。

十進位 二進位 十六進位
基數 =10 基數 =2 基數 =16
0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F
16 10000 10
17 10001 11

例如,觀察上表,我們可以看到在不同的計數系統中,相同的數字 11 可以表示不同的數字。如果基數是 2,那麼它指的是數字 3。如果基數是 16,那麼它指的是數字 17。

你可能已經注意到了,在我們的示例中,當輸入為 11 時,parseInt 返回 3,這對應於上表中的二進位列。

在 JavaScript 中,函數可以用任意數量的參數來調用,即使該數量不等於聲明函數時參數的數量。缺少的參數將被視為未定義,多餘的參數將被忽略(但是它們會被存儲在類似數組的參數對象中:https://javascriptweblog.wordpress.com/2011/01/18/javascripts-arguments-object-and-beyond/)。

function foo(x, y) {
    console.log(x);
    console.log(y);
}
foo(1, 2);
foo(1);
foo(1, 2, 3);

到了我們要講的部分了!

Map 是數組原型中的一個方法,它返回一個新的數組,其中包含將原始數組的每個元素傳遞到函數中的結果。例如,下面的代碼將數組中的每個元素乘以 3:

function multiplyBy3(x) {
    return x * 3;
}
const result = [1, 2, 3, 4, 5].map(multiplyBy3);
console.log(result);

現在,假設我想使用 map() 記錄每個元素(沒有返回語句)。我應該可以將 console.log 作為參數傳遞到 map() 中…... 對吧?

[1, 2, 3, 4, 5].map(console.log);

發生了一件非常奇怪的事情。每個 console.log 調用都記錄了索引和完整的數組,而不是只記錄值。

[1, 2, 3, 4, 5].map(console.log);

[1, 2, 3, 4, 5].map(
    (val, index, array) => console.log(val, index, array)
);

[1, 2, 3, 4, 5].map(
    val => console.log(val)
);

當一個函數被傳遞到 map() 中時,對於每個迭代,都會向函數中傳遞三個參數:currentValue 、currentIndex 和整個 array。這就是為什麼每次迭代都要記錄三個條目的原因。

我們現在有了解開這個謎團所需要的所有線索。

ParseInt 有兩個參數:string 和 radix 。如果提供的基數為假,那麼默認情況下,基數將被設置為 10。

parseInt('11');                => 11
parseInt('11', 2);             => 3
parseInt('11', 16);            => 17
parseInt('11', undefined);     => 11 (基數為假)
parseInt('11', 0);             => 11 (基數為假)

['1', '7', '11'].map(parseInt); => [1, NaN, 3]
第一次第一次迭代 index = 0, array = ['1', '7', '11']
parseInt('1', 0, ['1', '7', '11']); => 1

因為 0 是假,所以基數設置為默認值 10。parseInt() 只接受兩個參數,因此會忽略第三個參數 ['1', '7', '11'] 。以 10 為基數的字符串 '1' 表示數字 1。

// 第二次迭代 index = 1, array = ['1', '7', '11']
parseInt('7', 1, ['1', '7', '11']); => NaN

在基數為 1 的系統中,符號 '7' 不存在。與第一次迭代一樣,最後一個參數將被忽略。因此,parseInt() 返回 NaN。

// 第三次迭代, index = 2, array = ['1', '7', '11']
parseInt('11', 2, ['1', '7', '11']); => 3

在基數為 2(二進位)的系統中,符號 '11' 表示數字 3。最後一個參數被忽略。

['1', '7', '11'].map(parseInt) 不能按預期工作,是因為 map 在每次迭代時都會將三個參數傳遞到 parseInt() 中。第二個參數 index 作為 radix 參數傳遞給 parseInt。因此,數組中的每個字符串都使用不同的基數進行解析。'7' 按照基數 1 進行解析,即 NaN;'11' 按照基數 2 進行解析,即 3。'1' 按照默認基數 10 進行解析,因為它的索引 0 是假。

['1', '7', '11'].map(numStr => parseInt(numStr));

英文原文:https://medium.com/dailyjs/parseint-mystery-7c4368ef7b21

相關焦點

  • 2015:JavaScript 之於物聯網
    我們看到了很多物聯網設備在設備的尺寸和設備能力方面都有著非常大的進步。 包括像微軟、三星這樣的巨頭公司也先後正式進入了這個領域,物聯網社區在2015年整體上在朝著更加壯大的方向發展。 因此本文將會探討與總結物聯網和JavaScript技術在2015年的發展現狀。
  • 米線店來了奇怪顧客,反常的舉動讓人擔心,米線裡被下了藥?
    晚上九點臨汾路一家米線店,來了一位女顧客,種種行為舉動,都讓店員覺得很奇怪!
  • JavaScript原型:關於對象的深入研究
    原型屬性的分配機制是一個重要的概念,利用它可以在 JavaScript 中應用繼承。在理解原型之前,你必須了解 對象 的一些基本原理和用法。在本文中,我們將深入探討在對象創建過程中是如何分配原型的,以及為什麼這部分知識非常重要。 本文最初發布於 Aparna Joshi 網站,經原作者授權由 InfoQ 中文站翻譯並分享。
  • 喊話JavaScript開發者:玩DOM也要專業範兒
    2008 年,當我剛成為一名專業 Web 開發人員參加工作時,我了解一些 HTML、CSS 和 PHP 的知識。那時我也在學習 JavaScript,因為它可以用來顯示和隱藏元素和製作下拉菜單之類很酷的事情。當時我在一家小公司工作,我們主要為客戶創建 CMS 系統。彼時我們需要一個多文件上傳器,這是當時的原生 JavaScript 無法實現的功能。
  • Inn Community | Find Us on the Old Map
    the WeekMarch 27: Back to 1930s Shanghai~Can you find where our Mandarin Inn School was over 80 years ago on this map
  • 【編程1小時】將德國城市法蘭克福5月份溫度轉換為攝氏溫度,輸出最高、最低和平均溫度
    法蘭克福全城擁有超過324家銀行,經營著德國85%的股票交易和歐洲規模最大的國際性車展。flkf字符串記錄了2020年5月份法蘭克福天氣的最高溫度和最低溫度(單位為凱士溫度K),請編程將凱士溫度溫度轉換為攝氏溫度,輸出法蘭克福的最高、最低和平均溫度。
  • 西班牙天氣反常 平均氣溫比往年低10度
    據西班牙國家氣象局消息,今年西班牙各地平均氣溫比往年低了10度,進入六月以來,雨天甚至趕超冬季,還伴隨有大暴雨和大範圍降溫。國家氣象局數據顯示,今年全境平均氣溫低於25度,北部萊昂省、加利西亞省等地的氣溫更是徘徊在15度低溫,阿維拉16度、盧戈和塞哥維亞17度、薩拉曼卡和巴亞多利德等地18度。唯有薩拉戈薩氣溫最高,達到32度,其次是南部地區穆爾西亞30度。 氣象局發言人Ana Casals表示,本周末西班牙全境還會迎來新一輪降溫。
  • 經驗分享: 劍走偏鋒的程式設計師,如何用google map找到了工作
    後來才知道,Eric求職,是劍走偏鋒但人不順應主流,意味著要付出更多與其說Eric用google map
  • 學習Python的利器:內置函數dir()和help()
    在Python中所有的一切都是對象,除了整數、實數、複數、字符串、列表、元組、字典、集合等等,還有range對象、enumerate對象、zip對象、filter對象、map對象等等,函數也是對象,類也是對象,模塊也是對象。這樣的話,dir()的用武之地就大了。
  • Go語言無孔不入的2016:躋身主流程式語言、國內大熱、極速提升、尖端應用……
    從Go 1.6開始,我們其實已經可以間接地在Go程序中使用到HTTP/2了,應用場景如:使用Go程序開發基於HTTPS協議的服務端和客戶端。不過,這一切都是自動適配的,Go官方並未暴露出可以指定或配置HTTP/2模塊的任何API。另外,在還未發布的Go 1.8中,HTTP/2還會得到更廣泛的支持。
  • 夢見丨奇怪的車和人
    2020年11月27夢見奇怪的車和人昨夜夢見突然來到一輛銀灰色的小車上,我進來時,車正在行駛中(很奇怪,我是怎麼進去的)還坐在靠車尾的座位中,座位是這麼布置的:整個車空間,只有前左後有座位,一排2--3張,和駕駛艙位置。