parseInt (null, 24) === 23 ... เดี๋ยวก่อนอะไรนะ?


226

เอาล่ะฉันเลยไปยุ่งกับ parseInt เพื่อดูว่ามันจัดการกับค่าที่ยังไม่ได้เริ่มต้นและฉันสะดุดกับอัญมณีนี้ ด้านล่างเกิดขึ้นสำหรับ Radix ใด ๆ 24 หรือสูงกว่า

parseInt(null, 24) === 23 // evaluates to true

ฉันทดสอบใน IE, Chrome และ Firefox และพวกเขาทั้งหมดแจ้งเตือนจริงดังนั้นฉันคิดว่ามันต้องอยู่ในสเปคที่ไหนสักแห่ง การค้นหาโดย Google อย่างรวดเร็วไม่ได้ให้ผลลัพธ์ใด ๆ แก่ฉันดังนั้นฉันจึงหวังว่าจะมีคนอธิบายได้

ฉันจำได้ว่าฟังคำพูดของคร็อคฟอร์ดที่เขาพูดtypeof null === "object"เพราะการกำกับดูแลทำให้ Object และ Null มีตัวระบุประเภทใกล้เคียงกันในหน่วยความจำหรือบางอย่างในบรรทัดเหล่านั้น แต่ตอนนี้ฉันไม่พบวิดีโอนั้น

ลองทำดู: http://jsfiddle.net/robert/txjwP/

แก้ไขการแก้ไข: Radix ที่สูงกว่าให้ผลลัพธ์ที่แตกต่าง 32 ผลลัพธ์ 785077
แก้ไข 2จาก zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745


TL; DR

อธิบายว่าทำไมจึงparseInt(null, 24) === 23เป็นข้อความที่แท้จริง


49
วิธีเล่นโวหาร JavaScript ช่วยให้คุณอยู่เสมอ
FishBasketGordo

1
จุดข้อมูล: alert(parseInt(null, 34) === 23)ผลิตแล้วfalse
Stephen P

1
alert(parseInt(null,26)===23);ยังผลิตจริงหรือไม่!
Petar Ivanov

6
[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745,[37...]:NaN
zzzzBov

1
ในฐานะที่เป็นบันทึกเพิ่มเติมundefinedเป็นพารามิเตอร์แรกส่งกลับผลลัพธ์ที่แปลกสำหรับ 30
zzzzBov

คำตอบ:


240

มันกำลังแปลงnullเป็นสตริง"null"และพยายามแปลงมัน สำหรับ radixes 0 ถึง 23 NaNมีเลขมันสามารถแปลงไม่ดังนั้นก็จะส่งกลับ เมื่อถึง 24 "n"ตัวอักษรที่ 14 จะถูกเพิ่มเข้าไปในระบบเลข ที่ 31, "u"ตัวอักษรที่ 21, มีการเพิ่มและสตริงทั้งหมดสามารถถอดรหัส ที่ 37 บนจะไม่มีชุดตัวเลขที่ถูกต้องที่สามารถสร้างได้และ NaN จะถูกส่งคืน

js> parseInt(null, 36)
1112745

>>> reduce(lambda x, y: x * 36 + y, [(string.digits + string.lowercase).index(x) for x in 'null'])
1112745

3
@Tomalak: ใครบอกว่ามันใช้toString()?
Ignacio Vazquez-Abrams

3
@Ignacio ที่จริงฉันผิด ฉันไม่รู้ว่า 37 หมายถึงเลขฐาน ขอโทษด้วยกับเรื่องนั้น.
Mike Samuel

3
@ Robert ฉันไม่สับสนและคิดว่าเขาอ้างอะไรบางอย่างนอกเหนือจากสิ่งที่เขาอ้าง มันเป็นคำตอบที่ถูกต้อง ขอโทษรอบ ๆ
Mike Samuel

19
ฉันยังคิดว่าคำตอบนี้สามารถทำได้กับการอ้างอิงบางอย่าง แม้ว่ามันจะถูกต้องทั้งหมดมันเป็นเพียงแค่การยืนยันครั้งใหญ่ ...
Lightness Races in Orbit

4
@ Tomalak - ตรวจสอบคำตอบของฉันสำหรับการอ้างอิงทั้งหมด คำตอบนี้เป็นคำที่ถูกต้อง (และข้อแรก) ดังนั้นฉันคิดว่ามันควรจะเป็นคำตอบที่ได้รับการยอมรับ แม้ว่ามันจะไม่เจ็บที่จะอธิบายสิ่งที่เกิดขึ้นภายใต้ประทุน;)
David Titarenco

118

Mozilla บอกเรา :

function parseInt แปลงอาร์กิวเมนต์ตัวแรกเป็นสตริงวิเคราะห์คำแล้วส่งคืนเลขจำนวนเต็มหรือ NaN หากไม่ใช่ NaN ค่าที่ส่งคืนจะเป็นเลขจำนวนเต็มฐานสิบของอาร์กิวเมนต์แรกที่ใช้เป็นตัวเลขในฐานที่ระบุ (ฐาน) ตัวอย่างเช่นเลขฐานสิบของ 10 หมายถึงการแปลงจากจำนวนทศนิยม 8 ฐานแปด 16 ฐานสิบหกและอื่น ๆ สำหรับ radices ที่สูงกว่า 10 ตัวอักษรของตัวอักษรจะระบุตัวเลขที่มากกว่า 9 ตัวอย่างเช่นสำหรับเลขฐานสิบหก (ฐาน 16) จะใช้ A ถึง F

ในข้อมูลจำเพาะ 15.1.2.2/1 บอกเราว่าการแปลงเป็นสตริงดำเนินการโดยใช้บิวด์อินToStringซึ่ง (ตาม 9.8) ให้ผลผลิต"null"(เพื่อไม่ให้สับสนtoStringซึ่งจะให้ผลผลิต"[object Window]"!)

parseInt("null", 24)ดังนั้นขอพิจารณา

ของหลักสูตรนี้ไม่ได้เป็นฐาน-24 สตริงตัวเลขในสิ่งทั้งปวง แต่ "n" คือมันเป็นทศนิยม 23

ตอนนี้การแยกวิเคราะห์หยุดหลังจากดึงทศนิยม 23 ออกเนื่องจาก"u" ไม่พบในระบบฐาน -24:

หาก S มีอักขระใด ๆ ที่ไม่ใช่ตัวเลข radix-R ดังนั้นให้ Z เป็นสตริงย่อยของ S ซึ่งประกอบด้วยอักขระทั้งหมดก่อนอักขระตัวแรก มิฉะนั้นให้ Z เป็น S [15.1.2.2/11]

(และนี่คือเหตุผลที่parseInt(null, 23)(และ radices ที่ต่ำกว่า) ให้คุณNaNมากกว่า 23: "n"ไม่ได้อยู่ในระบบ base-23)


2
นี่เป็นพฤติกรรมที่น่าเศร้าของ parseInt (ฉันคิดว่าทำไมมันไม่ได้ออกแบบมาเพื่อยกเว้นในกรณีนี้) ฉันต้องการใช้ NUMBER () แทนเมื่อทำได้
Grijesh Chauhan

79

Ignacio Vazquez-Abrams ถูกต้องแล้ว แต่มาดูกันว่ามันทำงานอย่างไร ...

จาก15.1.2.2 parseInt (string , radix):

เมื่อเรียกใช้ฟังก์ชัน parseInt ขั้นตอนต่อไปนี้จะถูกนำมาใช้:

  • ให้ inputString เป็น ToString (สตริง)
  • ให้ S เป็นสตริงย่อยที่สร้างขึ้นใหม่ของ inputString ซึ่งประกอบด้วยอักขระตัวแรกที่ไม่ใช่ StrWhiteSpaceChar และอักขระทั้งหมดที่ตามหลังอักขระนั้น (กล่าวอีกนัยหนึ่งให้ลบช่องว่างนำหน้า)
  • อนุญาตให้ลงชื่อเป็น 1
  • หาก S ไม่ว่างเปล่าและอักขระตัวแรกของ S เป็นเครื่องหมายลบ - ให้ลงนามเป็น −1
  • หาก S ไม่ว่างเปล่าและอักขระตัวแรกของ S คือเครื่องหมายบวก + หรือเครื่องหมายลบ - ให้ลบอักขระตัวแรกออกจาก S
  • ให้ R = ToInt32 (radix)
  • ให้ stripPrefix เป็นจริง
  • ถ้า R ≠ 0 ดังนั้น a ถ้า R <2 หรือ R> 36 ให้ส่งคืน NaN ข หาก R ≠ 16 ให้ stripPrefix เป็นเท็จ
  • อื่น R = 0 a ให้ R = 10
  • หาก stripPrefix เป็นจริงดังนั้น หากความยาวของ S เป็นอย่างน้อย 2 และอักขระสองตัวแรกของ S เป็น“ 0x” หรือ“ 0X” ให้ลบอักขระสองตัวแรกออกจาก S แล้วปล่อยให้ R = 16
  • หาก S มีอักขระใด ๆ ที่ไม่ใช่ตัวเลข radix-R ดังนั้นให้ Z เป็นสตริงย่อยของ S ซึ่งประกอบด้วยอักขระทั้งหมดก่อนอักขระตัวแรก มิฉะนั้นให้ Z เป็น S
  • ถ้า Z ว่างเปล่าส่งคืน NaN
  • ให้ mathInt เป็นค่าจำนวนเต็มทางคณิตศาสตร์ที่แสดงด้วย Z ในรูปสัญลักษณ์ radix-R โดยใช้ตัวอักษร AZ และ az สำหรับตัวเลขที่มีค่า 10 ถึง 35 (อย่างไรก็ตามถ้า R คือ 10 และ Z มีจำนวนนัยสำคัญมากกว่า 20 หลักทุกสำคัญ ตัวเลขหลังวันที่ 20 อาจถูกแทนที่ด้วย 0 หลักที่ตัวเลือกของการดำเนินการและถ้า R ไม่ได้เป็น 2, 4, 8, 10, 16, หรือ 32 แล้ว mathInt อาจเป็นการประมาณขึ้นอยู่กับการดำเนินการกับจำนวนเต็มทางคณิตศาสตร์ ค่าที่แสดงโดย Z ในรูปแบบ radix-R)
  • ให้ number เป็นค่า Number สำหรับ mathInt
  • กลับเครื่องหมาย×จำนวน

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

ที่นี่มีสองส่วนที่สำคัญ ฉันกล้าทั้งสองอย่าง อย่างแรกเลยเราต้องหาว่าการtoStringแทนค่าnullคืออะไร เราจำเป็นต้องดูTable 13 — ToString Conversionsในส่วน 9.8.0 สำหรับข้อมูลนั้น:

ป้อนคำอธิบายรูปภาพที่นี่

เยี่ยมมากตอนนี้เรารู้แล้วว่าการทำtoString(null)ภายในให้'null'สตริง เยี่ยมมาก แต่มันจัดการกับตัวเลข (ตัวอักษร) ที่ไม่ถูกต้องภายใน radix ที่ให้ไว้ได้อย่างไร

เรามองไปข้างหน้า15.1.2.2และเราเห็นคำพูดต่อไปนี้:

หาก S มีอักขระใด ๆ ที่ไม่ใช่ตัวเลข radix-R ดังนั้นให้ Z เป็นสตริงย่อยของ S ซึ่งประกอบด้วยอักขระทั้งหมดก่อนอักขระตัวแรก มิฉะนั้นให้ Z เป็น S

นั่นหมายความว่าเราจัดการตัวเลขทั้งหมดก่อนหน้าไปยังฐานที่กำหนดและไม่สนใจทุกอย่างอื่น

โดยทั่วไปทำคือสิ่งเดียวกับparseInt(null, 23) ทำให้สอง's ที่จะละเลย (ถึงแม้พวกเขาเป็นส่วนหนึ่งของสมุฎฐาน 23) ดังนั้นเราเท่านั้นสามารถแยกทำให้ความหมายเหมือนคำสั่งทั้งหมด :)parseInt('null', 23)ulnparseInt('n', 23)

ทั้งสองวิธีคำถามที่ยอดเยี่ยม!


33
parseInt( null, 24 ) === 23

มีค่าเท่ากับ

parseInt( String(null), 24 ) === 23

ซึ่งเทียบเท่ากับ

parseInt( "null", 24 ) === 23

ตัวเลขสำหรับฐาน 24 คือ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, ... , n

สเป็คภาษาพูดว่า

  1. หาก S มีอักขระใด ๆ ที่ไม่ใช่ตัวเลข radix-R ดังนั้นให้ Z เป็นสตริงย่อยของ S ซึ่งประกอบด้วยอักขระทั้งหมดก่อนอักขระตัวแรก มิฉะนั้นให้ Z เป็น S

ซึ่งเป็นส่วนที่ทำให้มั่นใจได้ว่าตัวอักษรจำนวนเต็ม C-style เหมือน15Lแจงอย่างถูกต้องดังนั้นข้างต้นเทียบเท่ากับ

parseInt( "n", 24 ) === 23

"n" เป็นตัวอักษรที่ 23 ของรายการหลักด้านบน

QED


16

ฉันเดาได้รับการแปลงสตริงnull "null"ดังนั้นnจริง ๆ แล้ว23ใน 'base24' (เหมือนกันใน 'base25' +), uไม่ถูกต้องใน 'base24' ดังนั้นส่วนที่เหลือของสตริงnullจะถูกละเว้น นั่นเป็นสาเหตุที่มันส่งออก23จนกว่าuจะมีผลใน 'base31'


7

parseInt ใช้การแสดงตัวเลขและตัวอักษรจากนั้นในฐาน -24 "n" นั้นถูกต้อง แต่ "u" เป็นอักขระที่ไม่ถูกต้องจากนั้น parseInt จะแยกวิเคราะห์เฉพาะค่า "n" ....

parseInt("n",24) -> 23

ตัวอย่างลองกับสิ่งนี้:

alert(parseInt("3x", 24))

ผลลัพธ์จะเป็น "3"

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