คู่มือที่จำเป็นสำหรับประเภทข้อมูลใหม่ล่าสุดของ JavaScript: BigInt

เผยแพร่แล้ว: 2022-03-10
สรุปอย่างรวดเร็ว ↬ ใน JavaScript ประเภท Number ไม่สามารถแสดงค่าจำนวนเต็มที่มากกว่า 2 53 ได้อย่างปลอดภัย ข้อจำกัดนี้บังคับให้นักพัฒนาใช้วิธีแก้ปัญหาชั่วคราวและไลบรารีของบุคคลที่สามที่ไม่มีประสิทธิภาพ BigInt เป็นประเภทข้อมูลใหม่ที่มีจุดประสงค์เพื่อแก้ไขปัญหานั้น

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

นอกจากนี้ คุณสามารถทำงานกับการประทับเวลาที่มีความละเอียดสูง รหัสจำนวนเต็มขนาดใหญ่ และอื่นๆ ได้อย่างปลอดภัยโดยไม่ต้องใช้วิธีแก้ปัญหาชั่วคราว BigInt กำลังเป็นข้อเสนอระยะที่ 3 เมื่อเพิ่มในข้อกำหนดแล้ว มันจะกลายเป็นประเภทข้อมูลตัวเลขที่สองใน JavaScript ซึ่งจะทำให้จำนวนรวมของประเภทข้อมูลที่รองรับเป็นแปด:

  • บูลีน
  • โมฆะ
  • ไม่ได้กำหนด
  • ตัวเลข
  • BigInt
  • สตริง
  • สัญลักษณ์
  • วัตถุ

ในบทความนี้ เราจะมาดู BigInt ให้ดีและดูว่าสามารถช่วยเอาชนะข้อจำกัดของประเภท Number ใน JavaScript ได้อย่างไร

ปัญหา

การขาดประเภทจำนวนเต็มที่ชัดเจนใน JavaScript มักจะสร้างความสับสนให้กับโปรแกรมเมอร์ที่มาจากภาษาอื่น ภาษาโปรแกรมหลายภาษารองรับตัวเลขหลายประเภท เช่น float, double, integer และ bignum แต่นั่นไม่ใช่กรณีของ JavaScript ใน JavaScript ตัวเลขทั้งหมดจะแสดงในรูปแบบจุดทศนิยม 64 บิตแบบ double-precision ตามที่กำหนดโดยมาตรฐาน IEEE 754-2008

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

ภายใต้มาตรฐานนี้ จำนวนเต็มขนาดใหญ่มากที่ไม่สามารถแสดงได้ทั้งหมดจะถูกปัดเศษโดยอัตโนมัติ เพื่อความชัดเจน ประเภท Number ใน JavaScript สามารถแสดงจำนวนเต็มได้อย่างปลอดภัยระหว่าง -9007199254740991 (-(2 53 -1)) และ 9007199254740991 (2 53 -1) เท่านั้น ค่าจำนวนเต็มใดๆ ที่ไม่ได้อยู่ในช่วงนี้อาจสูญเสียความแม่นยำ

สามารถตรวจสอบได้ง่าย ๆ โดยรันโค้ดต่อไปนี้:

 console.log(9999999999999999); // → 10000000000000000

จำนวนเต็มนี้มากกว่าจำนวนที่มากที่สุดที่ JavaScript สามารถแสดงด้วย Number primitive ได้อย่างน่าเชื่อถือ ดังนั้นจึงมีลักษณะโค้งมน การปัดเศษที่ไม่คาดคิดอาจทำให้ความน่าเชื่อถือและความปลอดภัยของโปรแกรมลดลง นี่เป็นอีกตัวอย่างหนึ่ง:

 // notice the last digits 9007199254740992 === 9007199254740993; // → true

JavaScript ให้ค่าคงที่ Number.MAX_SAFE_INTEGER ที่ช่วยให้คุณได้รับจำนวนเต็มที่ปลอดภัยสูงสุดใน JavaScript ได้อย่างรวดเร็ว ในทำนองเดียวกัน คุณสามารถรับจำนวนเต็มปลอดภัยขั้นต่ำได้โดยใช้ค่าคงที่ Number.MIN_SAFE_INTEGER :

 const minInt = Number.MIN_SAFE_INTEGER; console.log(minInt); // → -9007199254740991 console.log(minInt - 5); // → -9007199254740996 // notice how this outputs the same value as above console.log(minInt - 4); // → -9007199254740996

การแก้ไขปัญหา

เพื่อเป็นการแก้ปัญหาชั่วคราวสำหรับข้อจำกัดเหล่านี้ นักพัฒนา JavaScript บางคนจะแสดงจำนวนเต็มขนาดใหญ่โดยใช้ประเภท String ตัวอย่างเช่น Twitter API เพิ่ม ID เวอร์ชันสตริงให้กับออบเจ็กต์เมื่อตอบสนองด้วย JSON นอกจากนี้ ไลบรารีจำนวนหนึ่ง เช่น bignumber.js ได้รับการพัฒนาเพื่อให้ทำงานกับจำนวนเต็มขนาดใหญ่ได้ง่ายขึ้น

ด้วย BigInt แอปพลิเคชันไม่ต้องการวิธีแก้ปัญหาหรือไลบรารีอีกต่อไปในการแสดงจำนวนเต็มที่เกิน Number.MAX_SAFE_INTEGER และ Number.Min_SAFE_INTEGER อย่างปลอดภัย ขณะนี้การดำเนินการเลขคณิตกับจำนวนเต็มขนาดใหญ่สามารถทำได้ใน JavaScript มาตรฐานโดยไม่สูญเสียความแม่นยำ ประโยชน์เพิ่มเติมของการใช้ชนิดข้อมูลดั้งเดิมบนไลบรารีของบุคคลที่สามคือประสิทธิภาพรันไทม์ที่ดีขึ้น

ในการสร้าง BigInt เพียงต่อท้าย n ต่อท้ายจำนวนเต็ม เปรียบเทียบ:

 console.log(9007199254740995n); // → 9007199254740995n console.log(9007199254740995); // → 9007199254740996

หรือคุณสามารถเรียกคอนสตรัคเตอร์ BigInt() :

 BigInt("9007199254740995"); // → 9007199254740995n

สามารถเขียนตัวอักษร BigInt ในรูปแบบไบนารี ฐานแปด หรือเลขฐานสิบหกได้:

 // binary console.log(0b100000000000000000000000000000000000000000000000000011n); // → 9007199254740995n // hex console.log(0x20000000000003n); // → 9007199254740995n // octal console.log(0o400000000000000003n); // → 9007199254740995n // note that legacy octal syntax is not supported console.log(0400000000000000003n); // → SyntaxError

โปรดทราบว่าคุณไม่สามารถใช้ตัวดำเนินการความเท่าเทียมกันที่เข้มงวดเพื่อเปรียบเทียบ BigInt กับตัวเลขปกติได้ เนื่องจากไม่ใช่ประเภทเดียวกัน:

 console.log(10n === 10); // → false console.log(typeof 10n); // → bigint console.log(typeof 10); // → number

คุณสามารถใช้โอเปอเรเตอร์ความเท่าเทียมกันซึ่งทำการแปลงประเภทโดยนัยก่อนที่จะทำการเทียบตัวถูกดำเนินการ:

 console.log(10n == 10); // → true

ตัวดำเนินการเลขคณิตทั้งหมดสามารถใช้ได้กับ BigInt ยกเว้นตัวดำเนินการ unary plus ( + ):

 10n + 20n; // → 30n 10n - 20n; // → -10n +10n; // → TypeError: Cannot convert a BigInt value to a number -10n; // → -10n 10n * 20n; // → 200n 20n / 10n; // → 2n 23n % 10n; // → 3n 10n ** 3n; // → 1000n let x = 10n; ++x; // → 11n --x; // → 10n

สาเหตุที่ไม่รองรับโอเปอเรเตอร์ unary plus ( + ) คือบางโปรแกรมอาจใช้ค่าคงที่ที่ + สร้าง Number เสมอ หรือมีข้อยกเว้น การเปลี่ยนพฤติกรรมของ + จะทำให้โค้ด asm.js เสียหายด้วย

โดยปกติ เมื่อใช้กับตัวถูกดำเนินการ BigInt ตัวดำเนินการเลขคณิตควรส่งคืนค่า BigInt ดังนั้น ผลลัพธ์ของตัวดำเนินการหาร ( / ) จะถูกตัดทอนโดยอัตโนมัติ ตัวอย่างเช่น:

 25 / 10; // → 2.5 25n / 10n; // → 2n

การแปลงประเภทโดยนัย

เนื่องจากการแปลงประเภทโดยนัยอาจสูญเสียข้อมูล จึงไม่อนุญาตการดำเนินการแบบผสมระหว่าง BigInt และ Number เมื่อผสมจำนวนเต็มขนาดใหญ่และตัวเลขทศนิยม ค่าผลลัพธ์อาจไม่สามารถแทนค่าได้อย่างถูกต้องโดย BigInt หรือ Number พิจารณาตัวอย่างต่อไปนี้:

 (9007199254740992n + 1n) + 0.5

ผลลัพธ์ของนิพจน์นี้อยู่นอกโดเมนของทั้ง BigInt และ Number ไม่สามารถแปลง Number ที่มีเศษส่วนเป็น BigInt ได้อย่างถูกต้อง และ BigInt ที่มากกว่า 2 53 ไม่สามารถแปลงเป็น Number ได้อย่างถูกต้อง

จากข้อจำกัดนี้ จึงไม่สามารถทำการดำเนินการเลขคณิตด้วยตัวถูกดำเนินการ Number และ BigInt ผสมกัน คุณยังไม่สามารถส่ง BigInt ไปยัง Web API และฟังก์ชัน JavaScript ในตัวที่คาดหวัง Number ความพยายามที่จะทำเช่นนั้นจะทำให้เกิด TypeError :

 10 + 10n; // → TypeError Math.max(2n, 4n, 6n); // → TypeError

โปรดทราบว่าตัวดำเนินการเชิงสัมพันธ์ไม่ปฏิบัติตามกฎนี้ ดังที่แสดงในตัวอย่างนี้:

 10n > 5; // → true

หากคุณต้องการคำนวณเลขคณิตด้วย BigInt และ Number คุณต้องกำหนดโดเมนที่จะดำเนินการดังกล่าวก่อน ในการทำเช่นนั้น เพียงแค่แปลงตัวถูกดำเนินการอย่างใดอย่างหนึ่งโดยเรียก Number() หรือ BigInt() :

 BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20

เมื่อพบในบริบท Boolean BigInt จะได้รับการปฏิบัติเหมือนกับ Number กล่าวอีกนัยหนึ่ง BigInt ถือเป็นค่าความจริงตราบใดที่ไม่ใช่ 0n :

 if (5n) { // this code block will be executed } if (0n) { // but this code block won't }

ไม่มีการแปลงประเภทโดยนัยระหว่างประเภท BigInt และ Number เมื่อเรียงลำดับอาร์เรย์:

 const arr = [3n, 4, 2, 1n, 0, -1n]; arr.sort(); // → [-1n, 0, 1n, 2, 3n, 4]

ตัวดำเนินการระดับบิต เช่น | , & , << , >> และ ^ ทำงานบน BigInt ในลักษณะเดียวกันกับ Number s ตัวเลขติดลบจะถูกตีความว่าเป็นส่วนประกอบสองส่วนที่มีความยาวของอนันต์ ไม่อนุญาตให้ใช้ตัวถูกดำเนินการแบบผสม นี่คือตัวอย่างบางส่วน:

 90 | 115; // → 123 90n | 115n; // → 123n 90n | 115; // → TypeError

ตัวสร้าง BigInt

เช่นเดียวกับประเภทดั้งเดิมอื่น ๆ BigInt สามารถสร้างได้โดยใช้ฟังก์ชันตัวสร้าง อาร์กิวเมนต์ที่ส่งไปยัง BigInt() จะถูกแปลงเป็น BigInt โดยอัตโนมัติ ถ้าเป็นไปได้:

 BigInt("10"); // → 10n BigInt(10); // → 10n BigInt(true); // → 1n

ชนิดข้อมูลและค่าที่ไม่สามารถแปลงได้มีข้อยกเว้น:

 BigInt(10.2); // → RangeError BigInt(null); // → TypeError BigInt("abc"); // → SyntaxError

คุณสามารถดำเนินการทางคณิตศาสตร์ได้โดยตรงบน BigInt ที่สร้างขึ้นโดยใช้ตัวสร้าง:

 BigInt(10) * 10n; // → 100n

เมื่อใช้เป็นตัวถูกดำเนินการของตัวดำเนินการความเท่าเทียมกันที่เข้มงวด BigInt ที่สร้างขึ้นโดยใช้ตัวสร้างจะได้รับการปฏิบัติเหมือนกับตัวดำเนินการปกติ:

 BigInt(true) === 1n; // → true

ฟังก์ชั่นห้องสมุด

JavaScript จัดเตรียมฟังก์ชันไลบรารีสองแบบสำหรับแสดงค่า BigInt เป็นจำนวนเต็มที่ลงนามหรือไม่ได้ลงนาม:

  • BigInt.asUintN(width, BigInt) : ล้อม BigInt ระหว่าง 0 ถึง 2 ความกว้าง -1
  • BigInt.asIntN(width, BigInt) : ล้อม BigInt ระหว่าง -2 width-1 และ 2 width-1 -1

ฟังก์ชันเหล่านี้มีประโยชน์อย่างยิ่งเมื่อดำเนินการคำนวณแบบ 64 บิต วิธีนี้ทำให้คุณสามารถอยู่ในขอบเขตที่ต้องการได้

การสนับสนุนเบราว์เซอร์และการถ่ายทอด

ในขณะที่เขียนบทความนี้ Chrome +67 และ Opera +54 สนับสนุนประเภทข้อมูล BigInt อย่างสมบูรณ์ ขออภัย Edge และ Safari ยังไม่ได้ใช้งาน Firefox ไม่รองรับ BigInt โดยค่าเริ่มต้น แต่สามารถเปิดใช้งานได้โดยการตั้งค่า javascript.options.bigint true ใน about:config รายการเบราว์เซอร์ที่รองรับล่าสุดมีอยู่ใน Can I use….

โชคไม่ดีที่การ transpiling BigInt เป็นกระบวนการที่ซับซ้อนอย่างยิ่ง ซึ่งทำให้ต้องเสียค่าปรับประสิทธิภาพการทำงานรันไทม์อย่างมาก นอกจากนี้ยังเป็นไปไม่ได้ที่จะเติม BigInt โดยตรง เนื่องจากข้อเสนอเปลี่ยนพฤติกรรมของโอเปอเรเตอร์ที่มีอยู่หลายตัว สำหรับตอนนี้ ทางเลือกที่ดีกว่าคือการใช้ไลบรารี JSBI ซึ่งเป็นการนำ JavaScript ไปใช้งานจริงของข้อเสนอ BigInt

ไลบรารีนี้จัดเตรียม API ที่ทำงานเหมือนกับ BigInt ดั้งเดิมทุกประการ คุณสามารถใช้ JSBI ได้อย่างไร:

 import JSBI from './jsbi.mjs'; const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER); const b2 = JSBI.BigInt('10'); const result = JSBI.add(b1, b2); console.log(String(result)); // → '9007199254741001'

ข้อดีของการใช้ JSBI คือเมื่อเบราว์เซอร์รองรับแล้ว คุณไม่จำเป็นต้องเขียนโค้ดใหม่ คุณสามารถคอมไพล์โค้ด JSBI ของคุณเป็นโค้ดเนทีฟ BigInt ได้โดยอัตโนมัติโดยใช้ปลั๊กอิน Babel นอกจากนี้ ประสิทธิภาพของ JSBI ยังเทียบเท่ากับการใช้งาน BigInt ดั้งเดิม คุณสามารถคาดหวังการสนับสนุนเบราว์เซอร์ที่กว้างขึ้นสำหรับ BigInt ในไม่ช้า

บทสรุป

BigInt เป็นชนิดข้อมูลใหม่ที่มีไว้สำหรับใช้เมื่อค่าจำนวนเต็มมากกว่าช่วงที่ชนิดข้อมูล Number รองรับ ชนิดข้อมูลนี้ช่วยให้เราสามารถดำเนินการทางคณิตศาสตร์กับจำนวนเต็มขนาดใหญ่ แสดงการประทับเวลาที่มีความละเอียดสูง ใช้ ID จำนวนเต็มขนาดใหญ่ และอื่นๆ ได้อย่างปลอดภัยโดยไม่จำเป็นต้องใช้ไลบรารี

สิ่งสำคัญคือต้องจำไว้ว่าคุณไม่สามารถดำเนินการทางคณิตศาสตร์ด้วยตัวถูกดำเนินการ Number และ BigInt ผสมกัน คุณจะต้องกำหนดโดเมนที่จะดำเนินการโดยการแปลงตัวถูกดำเนินการอย่างใดอย่างหนึ่งอย่างชัดเจน นอกจากนี้ ด้วยเหตุผลด้านความเข้ากันได้ คุณไม่ได้รับอนุญาตให้ใช้ตัวดำเนินการ unary plus ( + ) บน BigInt

คุณคิดอย่างไร? คุณพบว่า BigInt มีประโยชน์หรือไม่? แจ้งให้เราทราบในความคิดเห็น!