ทำไม parseInt ให้ผล NaN ด้วย Array # map?


291

จากMozilla Developer Network :

[1,4,9].map(Math.sqrt)

จะให้ผลผลิต:

[1,2,3]

ทำไมจึงทำเช่นนี้:

['1','2','3'].map(parseInt)

ให้สิ่งนี้:

[1, NaN, NaN]

ฉันได้ทดสอบใน Firefox 3.0.1 และ Chrome 0.3 และเช่นเดียวกับข้อจำกัดความรับผิดชอบฉันรู้ว่านี่ไม่ใช่ฟังก์ชั่นข้ามเบราว์เซอร์ (ไม่มี IE)

ฉันพบว่าสิ่งต่อไปนี้จะบรรลุผลตามที่ต้องการ parseIntแต่ก็ยังคงไม่ได้อธิบายพฤติกรรมการกระทำผิดของ

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]

15
สำหรับสันหลังยาว: ใช้.map(parseFloat)เพราะใช้พารามิเตอร์เดียว

63
.map(Number)หรือการใช้งาน
นิโคไล

2
คุณสามารถ arr.map (Math.floor) ถ้าคุณต้องการ Integers โดยไม่ต้องใช้มือหมุน
dandavis

@Nikolai user669677 ข้อเสนอแนะยอดเยี่ยม! ฉันอยากโหวตว่าใน anwser
BiAiB

ทุกคนสามารถอธิบายได้ว่าทำไม parseInt แยกหมายเลขแรกได้อย่างถูกต้องและทำให้ข้อผิดพลาดอื่น ๆ กว่าดัชนีแรก
Bawa กรัม

คำตอบ:


478

ฟังก์ชัน callback in Array.mapมีพารามิเตอร์สามตัว :

จากหน้า Mozillaเดียวกันที่คุณเชื่อมโยงกับ:

การเรียกกลับถูกเรียกด้วยสามอาร์กิวเมนต์: ค่าขององค์ประกอบ, ดัชนีขององค์ประกอบและวัตถุ Array ที่ถูกสำรวจ "

ดังนั้นถ้าคุณเรียกฟังก์ชันparseIntที่คาดว่าจะมีอาร์กิวเมนต์สองตัวอาร์กิวเมนต์ที่สองจะเป็นดัชนีขององค์ประกอบ

ในกรณีนี้คุณจะสิ้นสุดการโทรparseIntด้วย radix 0, 1 และ 2 ตามลำดับ อันแรกเหมือนกันกับที่ไม่ได้จัดหาพารามิเตอร์ดังนั้นมันจึงเป็นค่าดีฟอลต์จากอินพุต (ฐาน 10 ในกรณีนี้) ฐาน 1 เป็นฐานตัวเลขที่เป็นไปไม่ได้และ 3 ไม่ใช่หมายเลขที่ถูกต้องในฐาน 2:

parseInt('1', 0); // OK - gives 1
parseInt('2', 1); // FAIL - 1 isn't a legal radix
parseInt('3', 2); // FAIL - 3 isn't legal in base 2 

ดังนั้นในกรณีนี้คุณต้องใช้ฟังก์ชัน wrapper:

['1','2','3'].map(function(num) { return parseInt(num, 10); });

หรือด้วยไวยากรณ์ ES2015 +:

['1','2','3'].map(num => parseInt(num, 10));

(ในทั้งสองกรณีเป็นการดีที่สุดที่จะให้ Radix อย่างชัดเจนparseIntตามที่แสดงเพราะไม่เช่นนั้นมันจะทำการเดา Radix ตามอินพุต) ในเบราว์เซอร์รุ่นเก่าบางรุ่น 0 ที่เป็นผู้นำทำให้เกิดการเดาเลขฐานแปดซึ่งมีแนวโน้มว่าจะเป็นปัญหา เดา hex ถ้าสตริงเริ่มต้นด้วย0x)


25

mapมีการส่งผ่านอาร์กิวเมนต์ที่ 2 ซึ่ง (ในหลาย ๆ กรณี) ทำให้parseIntพารามิเตอร์ radix ยุ่งเหยิง

หากคุณใช้เครื่องหมายขีดล่างคุณสามารถทำได้:

['10','1','100'].map(_.partial(parseInt, _, 10))

หรือไม่มีขีดล่าง:

['10','1','100'].map(function(x) { return parseInt(x, 10); });


18

คุณสามารถแก้ปัญหานี้โดยใช้ฟังก์ชันNumberเป็นฟังก์ชัน iteratee:

var a = ['0', '1', '2', '10', '15', '57'].map(Number);

console.log(a);

หากไม่มีโอเปอเรเตอร์ใหม่คุณสามารถใช้ Number เพื่อทำการแปลงรูปแบบได้ อย่างไรก็ตามมันแตกต่างจาก parseInt: มันไม่ได้แยกสตริงและส่งกลับ NaN ถ้าไม่สามารถแปลงจำนวน ตัวอย่างเช่น

console.log(parseInt("19asdf"));
console.log(Number("19asf"));


11

ฉันจะเดิมพันว่ามันเป็นเรื่องตลกที่เกิดขึ้นกับพารามิเตอร์ที่สองของ parseInt คือ radix ทำไมมันถึงทำลายด้วยการใช้ Array.map และไม่เมื่อคุณเรียกมันโดยตรงฉันไม่รู้

//  Works fine
parseInt( 4 );
parseInt( 9 );

//  Breaks!  Why?
[1,4,9].map( parseInt );

//  Fixes the problem
[1,4,9].map( function( num ){ return parseInt( num, 10 ) } );

1
parseInt จะถือว่าเป็นเลขฐานแปดถ้าสตริงที่ให้มาเริ่มต้นด้วยอักขระ 0 ตัว
Alnitak

โอ้ ... ถูกต้องแล้ว มันพยายามที่จะ "เดา" ฐานตามอินพุต ขอโทษด้วยกับเรื่องนั้น.
Peter Bailey

3

คุณสามารถใช้ฟังก์ชั่นลูกศร ES2015 / ES6 และเพียงส่งหมายเลขไปที่ parseInt ค่าเริ่มต้นสำหรับ radix จะเท่ากับ 10

[10, 20, 30].map(x => parseInt(x))

หรือคุณสามารถระบุ radix อย่างชัดเจนเพื่อให้อ่านรหัสได้ง่ายขึ้น

[10, 20, 30].map(x => parseInt(x, 10))

ในตัวอย่างข้างต้น radix ตั้งค่าเป็น 10 อย่างชัดเจน


1

การแก้ไขด่วนอื่น (ที่ทำงาน):

var parseInt10 = function(x){return parseInt(x, 10);}

['0', '1', '2', '10', '15', '57'].map(parseInt10);
//[0, 1, 2, 10, 15, 57]

0

parseIntควรหลีกเลี่ยง IMHO ด้วยเหตุผลนี้ คุณสามารถห่อเพื่อทำให้ปลอดภัยยิ่งขึ้นในบริบทเหล่านี้:

const safe = {
  parseInt: (s, opt) => {
    const { radix = 10 } = opt ? opt : {};
    return parseInt(s, radix);
  }
}

console.log( ['1','2','3'].map(safe.parseInt) );
console.log(
  ['1', '10', '11'].map(e => safe.parseInt(e, { radix: 2 }))
);

lodash / fp จะกำหนดให้อาร์กิวเมนต์เป็น 1 โดยค่าเริ่มต้นเพื่อหลีกเลี่ยง gotchas เหล่านี้ โดยส่วนตัวฉันได้พบวิธีแก้ไขปัญหาเหล่านี้เพื่อสร้างข้อบกพร่องให้มากที่สุดเท่าที่พวกเขาหลีกเลี่ยง ขึ้นบัญชีดำparseIntในความโปรดปรานของการดำเนินงานที่ปลอดภัยคือผมคิดว่าวิธีการที่ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.