BigInt
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2020.
Giá trị BigInt đại diện cho các giá trị số nguyên quá lớn hoặc quá nhỏ để có thể biểu diễn bằng kiểu primitive number.
Mô tả
Một giá trị BigInt, đôi khi còn được gọi đơn giản là BigInt, là một primitive bigint, được tạo bằng cách thêm n vào cuối một số nguyên literal, hoặc bằng cách gọi hàm BigInt() (không có toán tử new) và truyền cho nó một giá trị số nguyên hoặc giá trị chuỗi.
const previouslyMaxSafeInteger = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// 9007199254740991n
const hugeString = BigInt("9007199254740991");
// 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff");
// 9007199254740991n
const hugeOctal = BigInt("0o377777777777777777");
// 9007199254740991n
const hugeBin = BigInt(
"0b11111111111111111111111111111111111111111111111111111",
);
// 9007199254740991n
Các giá trị BigInt tương tự như các giá trị Number theo một số cách, nhưng cũng khác nhau ở một vài điểm quan trọng: Giá trị BigInt không thể được dùng với các phương thức trong đối tượng Math tích hợp sẵn và không thể trộn lẫn với giá trị Number trong các phép tính; chúng phải được ép kiểu về cùng một kiểu. Tuy nhiên, hãy cẩn thận khi ép kiểu qua lại, vì độ chính xác của giá trị BigInt có thể bị mất khi nó được ép kiểu thành giá trị Number.
Thông tin kiểu
Khi kiểm tra với typeof, một giá trị BigInt (primitive bigint) sẽ cho kết quả "bigint":
typeof 1n === "bigint"; // true
typeof BigInt("1") === "bigint"; // true
Một giá trị BigInt cũng có thể được bao bọc trong một Object:
typeof Object(1n) === "object"; // true
Toán tử
Hầu hết các toán tử đều hỗ trợ BigInt, tuy nhiên hầu hết không cho phép các toán hạng có kiểu hỗn hợp — cả hai toán hạng phải là BigInt hoặc không cái nào:
- Toán tử số học:
+,-,*,/,%,** - Toán tử bitwise:
>>,<<,&,|,^,~ - Phủ định đơn nguyên (
-) - Tăng/giảm:
++,--
Các toán tử trả về boolean cho phép trộn số và BigInt làm toán hạng:
- Toán tử quan hệ và toán tử bằng nhau:
>,<,>=,<=,==,!=,===,!== - Toán tử logic chỉ phụ thuộc vào tính truthy của các toán hạng
Một số toán tử không hỗ trợ BigInt:
- Cộng đơn nguyên (
+) không thể được hỗ trợ do xung đột cách dùng trong asm.js, vì vậy nó đã bị bỏ qua để không làm hỏng asm.js. - Dịch phải không dấu (
>>>) là toán tử bitwise duy nhất không được hỗ trợ, vì mọi giá trị BigInt đều có dấu.
Các trường hợp đặc biệt:
- Phép cộng (
+) liên quan đến chuỗi và BigInt trả về chuỗi. - Phép chia (
/) cắt bỏ phần thập phân về phía không, vì BigInt không thể biểu diễn số thập phân.
const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER); // 9007199254740991n
const maxPlusOne = previousMaxSafe + 1n; // 9007199254740992n
const theFuture = previousMaxSafe + 2n; // 9007199254740993n, this works now!
const prod = previousMaxSafe * 2n; // 18014398509481982n
const diff = prod - 10n; // 18014398509481972n
const mod = prod % 10n; // 2n
const bigN = 2n ** 54n; // 18014398509481984n
bigN * -1n; // -18014398509481984n
const expected = 4n / 2n; // 2n
const truncated = 5n / 2n; // 2n, not 2.5n
So sánh
Một giá trị BigInt không bằng nghiêm ngặt với giá trị Number, nhưng bằng theo nghĩa lỏng lẻo:
0n === 0; // false
0n == 0; // true
Giá trị Number và giá trị BigInt có thể được so sánh như bình thường:
1n < 2; // true
2n > 1; // true
2 > 2; // false
2n > 2; // false
2n >= 2; // true
Các giá trị BigInt và Number có thể trộn lẫn trong mảng và sắp xếp:
const mixed = [4n, 6, -12n, 10, 4, 0, 0n];
// [4n, 6, -12n, 10, 4, 0, 0n]
mixed.sort(); // default sorting behavior
// [ -12n, 0, 0n, 10, 4n, 4, 6 ]
mixed.sort((a, b) => a - b);
// won't work since subtraction will not work with mixed types
// TypeError: can't convert BigInt value to Number value
// sort with an appropriate numeric comparator
mixed.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
// [ -12n, 0, 0n, 4n, 4, 6, 10 ]
Lưu ý rằng các so sánh với các giá trị BigInt được bao bọc trong Object hoạt động như với các đối tượng khác, chỉ chỉ ra sự bằng nhau khi cùng một instance đối tượng được so sánh:
Object(0n) === 0n; // false
Object(0n) === Object(0n); // false
const o = Object(0n);
o === o; // true
Vì việc ép kiểu qua lại giữa giá trị Number và giá trị BigInt có thể dẫn đến mất độ chính xác, nên khuyến nghị như sau:
- Chỉ dùng giá trị BigInt khi các giá trị lớn hơn 253 được kỳ vọng một cách hợp lý.
- Không ép kiểu qua lại giữa giá trị BigInt và giá trị Number.
Điều kiện
Một giá trị BigInt tuân theo các quy tắc chuyển đổi tương tự như Number khi:
- nó được chuyển đổi thành
Boolean: thông qua hàmBoolean; - khi được dùng với toán tử logic
||,&&, và!; hoặc - trong một kiểm tra điều kiện như câu lệnh
if.
Cụ thể, chỉ có 0n là falsy; mọi thứ khác đều là truthy.
if (0n) {
console.log("Hello from the if!");
} else {
console.log("Hello from the else!");
}
// "Hello from the else!"
0n || 12n; // 12n
0n && 12n; // 0n
Boolean(0n); // false
Boolean(12n); // true
!12n; // false
!0n; // true
Mã hóa
Các phép tính được hỗ trợ trên giá trị BigInt không phải là constant-time và do đó dễ bị tấn công timing attack. Vì vậy, BigInt trong JavaScript có thể nguy hiểm khi dùng trong mã hóa mà không có các biện pháp giảm thiểu. Như một ví dụ rất chung, kẻ tấn công có thể đo sự chênh lệch thời gian giữa 101n ** 65537n và 17n ** 9999n, và suy ra độ lớn của các bí mật, chẳng hạn như khóa riêng tư, dựa trên thời gian trôi qua. Nếu bạn vẫn phải dùng BigInt, hãy xem Timing attack FAQ để biết lời khuyên chung về vấn đề này.
Sử dụng trong JSON
Sử dụng JSON.stringify() với bất kỳ giá trị BigInt nào sẽ gây ra TypeError, vì giá trị BigInt không được serialize trong JSON theo mặc định. Tuy nhiên, JSON.stringify() đặc biệt để lại một cửa hậu cho giá trị BigInt: nó sẽ thử gọi phương thức toJSON() của BigInt. (Nó không làm vậy với bất kỳ giá trị nguyên thủy nào khác.) Vì vậy, bạn có thể triển khai phương thức toJSON() của riêng mình (đây là một trong số ít trường hợp mà việc vá các đối tượng tích hợp sẵn không bị phản đối rõ ràng):
BigInt.prototype.toJSON = function () {
return { $bigint: this.toString() };
};
Thay vì ném lỗi, JSON.stringify() bây giờ tạo ra một chuỗi như thế này:
console.log(JSON.stringify({ a: 1n }));
// {"a":{"$bigint":"1"}}
Nếu bạn không muốn vá BigInt.prototype, bạn có thể dùng tham số replacer của JSON.stringify để serialize giá trị BigInt:
const replacer = (key, value) =>
typeof value === "bigint" ? { $bigint: value.toString() } : value;
const data = {
number: 1,
big: 18014398509481982n,
};
const stringified = JSON.stringify(data, replacer);
console.log(stringified);
// {"number":1,"big":{"$bigint":"18014398509481982"}}
Sau đó bạn có thể dùng tham số reviver của JSON.parse để xử lý chúng:
const reviver = (key, value) =>
value !== null &&
typeof value === "object" &&
"$bigint" in value &&
typeof value.$bigint === "string"
? BigInt(value.$bigint)
: value;
const payload = '{"number":1,"big":{"$bigint":"18014398509481982"}}';
const parsed = JSON.parse(payload, reviver);
console.log(parsed);
// { number: 1, big: 18014398509481982n }
Note:
Mặc dù có thể làm cho replacer của JSON.stringify() trở nên chung chung và serialize đúng các giá trị BigInt cho tất cả các đối tượng như đã trình bày ở trên, reviver của JSON.parse() phải được dùng cẩn thận, vì serialization là không thể đảo ngược: không thể phân biệt giữa một đối tượng tình cờ có thuộc tính tên $bigint và một BigInt thực sự.
Ngoài ra, ví dụ trên tạo ra toàn bộ đối tượng trong quá trình replacing và reviving, điều này có thể ảnh hưởng đến hiệu suất hoặc lưu trữ đối với các đối tượng lớn hơn chứa nhiều BigInt. Nếu bạn biết hình dạng của payload, tốt hơn là chỉ serialize chúng dưới dạng chuỗi và phục hồi chúng dựa trên tên key của thuộc tính.
Thực tế, JSON cho phép các số literal tùy ý dài; chúng chỉ không thể được phân tích với độ chính xác đầy đủ trong JavaScript. Nếu bạn đang giao tiếp với một chương trình khác bằng ngôn ngữ hỗ trợ số nguyên dài hơn (chẳng hạn như số nguyên 64-bit), và bạn muốn truyền BigInt dưới dạng số JSON thay vì chuỗi JSON, hãy xem Serialization số không mất mát.
Ép kiểu BigInt
Nhiều thao tác tích hợp sẵn kỳ vọng BigInt trước tiên ép kiểu đối số của chúng thành BigInt. Thao tác có thể được tóm tắt như sau:
- BigInt được trả về nguyên vẹn.
undefinedvànullném raTypeError.truechuyển thành1n;falsechuyển thành0n.- Chuỗi được chuyển đổi bằng cách phân tích chúng như thể chúng chứa một integer literal. Bất kỳ lỗi phân tích nào đều dẫn đến
SyntaxError. Cú pháp là một tập con của string numeric literals, trong đó dấu thập phân hoặc chỉ số mũ không được phép. - Số ném ra
TypeErrorđể ngăn việc ép kiểu ngầm định không có chủ ý gây mất độ chính xác. - Symbol ném ra
TypeError. - Đối tượng trước tiên được chuyển đổi thành primitive bằng cách gọi các phương thức
[Symbol.toPrimitive]()(với"number"làm hint),valueOf(), vàtoString()theo thứ tự đó. Primitive kết quả sau đó được chuyển đổi thành BigInt.
Cách tốt nhất để đạt được gần như cùng hiệu ứng trong JavaScript là thông qua hàm BigInt(): BigInt(x) dùng cùng thuật toán để chuyển đổi x, ngoại trừ Number không ném ra TypeError, mà được chuyển đổi thành BigInt nếu chúng là số nguyên.
Lưu ý rằng các thao tác tích hợp sẵn kỳ vọng BigInt thường cắt bớt BigInt xuống độ rộng cố định sau khi ép kiểu. Điều này bao gồm BigInt.asIntN(), BigInt.asUintN(), và các phương thức của BigInt64Array và BigUint64Array.
Constructor
BigInt()-
Trả về các giá trị nguyên thủy kiểu BigInt. Ném lỗi khi được gọi với
new.
Phương thức tĩnh
BigInt.asIntN()-
Kẹp một giá trị BigInt vào một giá trị số nguyên có dấu, và trả về giá trị đó.
BigInt.asUintN()-
Kẹp một giá trị BigInt vào một giá trị số nguyên không dấu, và trả về giá trị đó.
Thuộc tính instance
Các thuộc tính này được định nghĩa trên BigInt.prototype và được chia sẻ bởi tất cả các instance BigInt.
BigInt.prototype.constructor-
Hàm constructor đã tạo ra đối tượng instance. Đối với các instance
BigInt, giá trị ban đầu là constructorBigInt. BigInt.prototype[Symbol.toStringTag]-
Giá trị ban đầu của thuộc tính
[Symbol.toStringTag]là chuỗi"BigInt". Thuộc tính này được dùng trongObject.prototype.toString(). Tuy nhiên, vìBigIntcũng có phương thứctoString()riêng, thuộc tính này không được dùng trừ khi bạn gọiObject.prototype.toString.call()với một BigInt làmthisArg.
Phương thức instance
BigInt.prototype.toLocaleString()-
Trả về một chuỗi biểu diễn giá trị BigInt này nhạy cảm với ngôn ngữ. Ghi đè phương thức
Object.prototype.toLocaleString(). BigInt.prototype.toString()-
Trả về một chuỗi biểu diễn giá trị BigInt này theo cơ số (hệ) được chỉ định. Ghi đè phương thức
Object.prototype.toString(). BigInt.prototype.valueOf()-
Trả về giá trị BigInt này. Ghi đè phương thức
Object.prototype.valueOf().
Ví dụ
>Tính số nguyên tố
function isPrime(n) {
if (n < 2n) {
return false;
}
if (n % 2n === 0n) {
return n === 2n;
}
for (let factor = 3n; factor * factor <= n; factor += 2n) {
if (n % factor === 0n) {
return false;
}
}
return true;
}
// Takes a BigInt value as an argument, returns nth prime number as a BigInt value
function nthPrime(nth) {
let maybePrime = 2n;
let prime = 0n;
while (nth >= 0n) {
if (isPrime(maybePrime)) {
nth--;
prime = maybePrime;
}
maybePrime++;
}
return prime;
}
nthPrime(20n);
// 73n
Note:
Triển khai isPrime() chỉ dành cho mục đích minh họa. Với ứng dụng thực tế, bạn nên dùng một thuật toán được memoize nhiều như Sàng Eratosthenes để tránh tính toán lặp lại.
Đặc tả
| Thông số kỹ thuật |
|---|
| ECMAScript® 2027 Language Specification> # sec-bigint-objects> |