คู่มือที่จำเป็นสำหรับประเภทข้อมูลใหม่ล่าสุดของ JavaScript: BigInt
เผยแพร่แล้ว: 2022-03-10Number
ไม่สามารถแสดงค่าจำนวนเต็มที่มากกว่า 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
มีประโยชน์หรือไม่? แจ้งให้เราทราบในความคิดเห็น!