2023-06-21

4179

#vanilla javascript

Onuorah Bonaventure

172422

111

Jun 21, 2023 â‹… 14 min read

`BigInt`

Onuorah Bonaventure
Full-stack web developer that loves connecting brands to their customers by developing amazing, performant websites. Interested in teaching programming concepts and breaking down difficult concepts. When I'm not coding, I play chess and checkers with my friends while listening to cool Afrobeats.

Before the release of ES2020, there was no native way to use JavaScript to accurately represent and perform mathematical operations on numbers greater than `9,007,199,254,740,991`

or `253 â€“ 1`

, or less than `-9,007,199,254,740,991`

or `-(253 â€“ 1)`

. Most developers relied on libraries such as JSBI and bignumber.js to perform calculations on large numbers.

Fortunately, this situation gave rise to a data type called `BigInt`

. The introduction of `BigInt`

brought the total number of JavaScript data types to eight. In this article, we will learn what `BigInt`

is, its advantages, and how to use it in JavaScript properly. To learn more about representing large numbers in your Node.js applications, check out our guide here.

*Jump ahead:*

- What is
`BigInt`

? - Limitations of the
`Number`

type - Advantages of using
`BigInt`

- How to use
`BigInt`

in JavaScript - Working with
`BigInt`

methods `BigInt`

common rules and precautions- Limitations of
`BigInt`

- Browser and Node.js support

`BigInt`

?`BigInt`

is a numerical data type that can be used to represent both small and large numbers that cannot be represented by the older numerical data type, `number`

. Every `BigInt`

value must contain a lowercase `n`

after the number, e.g., `897n`

. Therefore, it is accurate to state that `21`

is not strictly equal to `21n`

because the former is a `number`

while the latter is a `bigint`

.

When you perform a mathematical operation on any number greater than `9,007,199,254,740,991`

or `Number.MAX_SAFE_INTEGER`

, you do not really get an accurate answer. We can see the demonstration below:

See the Pen

Bigint inllustrations and examples by Onuorah Bonaventure Chukwudi (@bonarhyme)

on CodePen.

However, we can use `BigInt`

to handle these kinds of operations accurately. To define an integer as `BigInt`

, we do either of the following methods:

- We could append
`n`

to a whole number, or - Call the
`BigInt()`

constructor on whole numerical values or strings with only integer values

They are demonstrated in the example below:

// Method 1 const sampleBigIntOne = 234n; // Method 2 const sampleBigIntTwo = BigInt(567) // This returns 567n const sampleBigIntThree = BigInt("123") // This return 123n const sampleBigIntFour = 12356789900099999999912111n // Still correct

To verify that a value is of the type `BigInt`

, we can use the `typeof`

operator, like this:

const sampleOne = 17n console.log(typeof sampleOne) // Expected result: "bigint" const sampleTwo = BigInt(789); console.log(typeof sampleTwo) // Expected result: "bigint" // This is a bad practise const sampleThree = typeof 17n === "bigint" console.log(sampleThree) // Expected result: true

N.B., passing numbers inside the`BigInt`

constructor is a bad practice. Instead, it is better to just append`n`

or to first wrap it in a string, as in`sampleBigIntOne`

and`sampleBigIntThree`

.

`BigInt`

`BigInt`

values find their use in applications that handle large numbers. Here are some key use cases of `BigInt`

:

**Financial calculations**:`BigInt`

is important in financial calculations because of the possibility of handling very large amounts of transactions and currency conversions**Cryptography and security computing**:`BigInts`

are used in cryptography to generate really large random numbers that are very difficult to predict or crack**Game developments**: In game developments, large numbers are usually used to hold timestamps, points, and to track progress. The use of`BigInt`

ensures the accurate representation of such values**Distributed systems**: Distributed systems require unique identities to perform accurately. Since`BigInt`

values are unlimited, they can be used to generate identifiers and keys

`BigInt`

is really important in these fields because it allows developers to safely and precisely handle huge integers, timestamps, IDs, and progress.

`BigInt`

vs. `Number`

Generally, a `number`

in JavaScript can be used to represent an integer and even a float, such as `69`

and `4.125677`

respectively. It includes values that range between `9,007,199,254,740,991`

or `253 â€“ 1`

and `-9,007,199,254,740,991`

or `-(253 â€“ 1)`

. These limits are available as constants, such as `Number.MAX_SAFE_INTEGER`

and `Number.MIN_SAFE_INTEGER`

, respectively.

As a matter of fact, you can perform all arithmetic operations, such as addition `(+)`

, subtraction `(-)`

, multiplication `(*)`

, division `(/)`

, remainder or modulo `(%)`

, and exponent `(**)`

on numbers in that range without any problems, but that isnâ€™t totally the case with `bigint`

.

`Bigint`

can be used to represent and perform operations on any size of integer except floats or decimals including numbers greater than `9,007,199,254,740,991`

or less than `-9,007,199,254,740,991`

. This means that decimal values such as `17.2n`

are invalid.

Generally, all the arithmetic operations except `(/)`

will give a correct mathematical answer when used between two or more `bigint`

values. For instance, the `division`

operator doesnâ€™t give an accurate result at all times. This means that operations such as `5n/2n`

will round off to `2n`

instead of `2.5n`

.

`Number`

typeThe JavaScript `number`

type is written as double-precision 64-bit binary format IEEE 754 value, meaning that 52 bits are used for significant values while one bit and 11 bits are used for the sign and exponents. Therefore, weâ€™ll likely face some limitations whenever we use `number`

. These limitations include:

- Round-off errors and Limited precision of numbers
- Inability to perform precise operations outside the minimum and maximum safe integers
- Failure to perform operations outside a
`MAX_VALUE`

and`MIN_VALUE`

range

JavaScript is bound to round off numbers because it uses 64 bits to represent a floating `number`

. This means that every number in JavaScript is first converted to a double-precision binary floating-point number before it is stored in the memory. In fact, the 64-bit representation of a number is actually divided into three parts, including the `significand`

, `biased exponent`

, and the `sign`

.

For example, a number such as `15`

looks like this after it is converted: `0.10000000010.1110000000000000000000000000000000000000000000000000`

N.B., you can go here to convert some numbers to a double-precision binary floating-point number

The first part of a double-precision binary floating point is the sign, which can be `0`

or `1`

, standing for a positive and negative sign of the number. So, it is positive when the sign is `0`

, and vice versa. It takes up 1 bit. The second part is the biased exponent, which takes up 11 bits, while the last part is the significand or mantissa, which takes up 52 bits.

Some numbers give an exact value, such as `1/2`

, `1/4`

, `1/5`

, `1/8`

, `1/10`

, and every whole number in the range of `9,007,199,254,740,991`

or `253 â€“ 1`

to `-9,007,199,254,740,991`

or `-(253 â€“ 1)`

. Some other numbers do not give exact values; instead, they are repeating decimals, such as `1/3`

, `1/6`

, `1/7`

, `1/9`

.

The problem lies in the fact that some of these decimal numbers lose precision when they are converted to binary and vice versa. So, when you take a decimal number, such as `0.1`

, and add it up to `0.2`

, they do not add up to `0.3`

. Instead, they add up to `0.30000000000000004`

. More examples can be seen below:

const sample1 = 0.2 + 0.6 console.log(sample1) // This returns 0.6000000000000001 instead of 0.6 const sample2 = 0.3 + 0.6 console.log(sample2) // This returns 0.8999999999999999 instead of 0.9

This is a huge problem when using numbers because it can pose a great risk in calculations that require high precision. This problem would be avoided when we use `bigint`

it because it doesnâ€™t accept decimals.

JavaScript numbers can only be precise when you perform an arithmetic operation between `9,007,199,254,740,991`

or `253 â€“ 1`

to `-9,007,199,254,740,991`

, or `-(253 â€“ 1)`

. This means that whatever operation you perform outside that range can result in a wrong answer, such as:

const sample3 = 9_007_199_254_740_991 + 1 // This gives 9,007,199,254,740,992 const sample4 = 9_007_199_254_740_991 + 2 // This gives 9,007,199,254,740,992 // However: console.log(sample3 === sample4) // Given result: true // Expected result: false

JavaScript also has a limit for the amount of numbers it can represent. In fact, the numbers are important, and they can be represented with two built-in constants: `Number.MAX_VALUE`

and `Number.MIN_VALUE`

. They are essentially the numbers: `1.7976931348623157e+308`

and `5e-324`

respectively.

Whenever you try to perform an addition or subtraction with `Number.MAX_VALUE`

, it returns `1.7976931348623157e+308`

without adding or subtracting any value. And when you try to do the same with `Number.MIN_VALUE`

, it returns the value as in the samples below:

const sample5 = Number.MAX_VALUE + 7 console.log(sample5) // Returns 1.7976931348623157e+308 const sample6 = Number.MAX_VALUE - 23 console.log(sample6) // Returns 1.7976931348623157e+308 const sample7 = Number.MIN_VALUE + 5 console.log(sample7) // Returns 5 const sample8 = Number.MIN_VALUE - 54 console.log(sample8) // Returns -54

It gets crazier when you perform a multiplication `(*)`

or exponent `(**)`

on `Number.MAX_VALUE`

as it always returns `Infinity`

. This value is represented as `Number.POSITIVE_INFINITY`

.

As you can see, only division works for `Number.MAX_VALUE`

and only multiplication works for `Number.MIN_VALUE`

. This is a serious limitation when you are required to perform calculations involving very high numbers. However, with the help of `BigInt`

, we can perform calculations on very high or low numbers.

`BigInt`

Now, letâ€™s dive into the advantages of using `BigInt`

in JavaScript. First, `BigInt`

ensures that we do not encounter round-off errors. When we work with numbers, we are allowed to use decimals which can lead to precision and round-off errors as we have seen in the previous section.

However, we arenâ€™t allowed to work with decimals when we use `BigInt`

. This ensures that such errors are avoided at all times. Therefore, it is invalid to do the following:

const sample1 = 5.4n; console.log(sample1) // This will throw a SyntaxError

`BigInt`

s also makes it possible to perform calculations with arbitrary precision. Unlike numbers that lose precision when we perform calculations outside the range of `Number.MAX_SAFE_INTEGER`

and `Number.MIN_SAFE_INTEGER`

, `BigInt`

allows us to perform such calculations without losing any precision. It can be shown in the example below:

var sample2 = BigInt(Number.MAX_SAFE_INTEGER) + 1n var sample3 = BigInt(Number.MAX_SAFE_INTEGER) + 2n console.log(sample2) // Expected result: 9007199254740992n console.log(sample3) // Expected result: 9007199254740993n // Therefore: console.log(sample3 > sample2) // Expected result: true

Interestingly, with `BigInt`

, we can safely and precisely perform calculations on integers that are bigger than `Number.MAX_VALUE`

because it has an arbitrary precision, meaning that the size of the integer it can perform operations on is only limited by the available memory on the host computer or system. This means that we cannot encounter the same problems when working with `BigInt`

:

const max = BigInt(Number.MAX_VALUE) console.log(max) // Expected Result: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368n const sample4 = max + 7 // Expected Result: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858375n const sample5 = max - 23 // Expected Result: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858345n

Additionally, `BigInt`

allows us to set a boundary on the number of bits we want to permit for a calculation. This means we can define the appropriate range of integers we want for our program. We can achieve this using the `asIntN`

and `asUintN`

methods that are available on the `BigInt`

prototype.

Essentially, the two methods are used to wrap a `BigInt`

value to a `width`

-digit binary signed and unsigned integer, respectively. It is worth noting that signed integers are used to represent negative values and vice versa.

Hence, if we want to limit our calculations to 8-bit, 16-bit, 32-bit, or 64-bit arithmetic, we can use the `asIntN`

method to achieve that by calling it completely with `BigInt.asIntN(width, value)`

. In this case, the `width`

parameter represents the desired bits that are greater than zero, whereas the `value`

parameter represents the `BigInt`

value we want to limit within the supplied `width`

.

Letâ€™s look at an example:

const maximumBits = 16; const valueOne = BigInt(32767); const constrainedValueOne = BigInt.asIntN(maximumBits, valueOne); console.log(constrainedValueOne); // Expected result: 32767n const valueTwo = BigInt(32768); const constrainedValueTwo = BigInt.asIntN(maximumBits, valueTwo); console.log(constrainedValueTwo) // Expected result: -32768n

As you can see in the example above, we created a 16-bit limit of integers we want to work with by using the `asIntN`

. Generally, 16-bits can only contain integers between `-32768n`

and `32767n`

. Hence, in the first example, it returned the value as is, whereas, in the latter, it truncated the value because it was outside the range we have specified.

- Don't miss a moment with The Replay, a curated newsletter from LogRocket
- Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
- Use React's useEffect to optimize your application's performance
- Switch between multiple versions of Node
- Discover how to use the React children prop with TypeScript
- Explore creating a custom mouse cursor with CSS
- Advisory boards arenâ€™t just for executives. Join LogRocketâ€™s Content Advisory Board. Youâ€™ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

`BigInt`

also improves speed in JavaScript applications. Before ES2020, developers relied on libraries such as Math.js and JSBI to calculate large numbers. However, some of these packages were heavy and slow. This caused applications and software built with JavaScript to perform slowly; however, the addition of `BigInt`

which is a native solution will allow the improvement of applications that perform calculations with large numbers.

And lastly, `BigInt`

can form a basis for the development of `BigDecimal`

. One core feature of `BigInt`

is that it doesnâ€™t permit the use of floats. However, floats come in handy in some aspects of development. Hence, the JavaScript specifications builders could take a hint and implement a native data type that could be used to represent floats that are precise and probably help us perform more accurate calculations, such as `0.1 + 0.2 = 0.3`

.

`BigInt`

in JavaScriptBecause `BigInt`

is a data type it is similar to numbers and can also be used to perform calculations. To define a `BigInt`

value, we can use `BigInt`

literal, which is `n`

, or use the `BigInt`

constructor on a string containing integers or on numbers.

However, we should be wary about coercing numbers to `BigInt`

using the constructor because numbers might lose precision even before the conversion occurs. Hereâ€™s what that looks like:

const dontDoThis = BigInt(12345567891234567890) console.log(dontDoThis) // Given result: 12345567891234568192n // Expected result: 12345567891234567890n const dontDoThisToo = BigInt(`${12345567891234567890}`) console.log(dontDoThis) // Given result: 12345567891234568000n // Expected result: 12345567891234567890n

As you can see in the examples above, we lost precision when we coerced a number to `BigInt`

. Therefore, we are advised not to do that, and should do this instead:

const doThis = BigInt("12345567891234567890") console.log(doThis) // Expected result: 12345567891234567890n const doThisAlso = 12345567891234567890n console.log(doThisAlso) // Expected result: 12345567891234567890n const numberInString = "12345567891234567890"; const doThisToo = BigInt(numberInString) console.log(doThisToo) // Expected result: 12345567891234567890n

`BigInt`

methodsThere are five different methods you can use with a call on the `BigInt`

class:

`BigInt.prototype.toString()`

`BigInt.prototype.valueOf()`

`BigInt.prototype.toLocaleString()`

`BigInt.asIntN()`

`BigInt.asUintN()`

Whenever you encounter a method like `BigInt.prototype.toString()`

, it means the method `toString()`

will be called on a `BigInt`

integer like `5n.toString()`

, whereas methods such as `BigInt.asUintN()`

will be called directly on the `BigInt`

constructor like this `BigInt.asIntN(maximumBits, valueTwo)`

`BigInt.prototype.toString()`

First, letâ€™s look at `BigInt.prototype.toString()`

. The `toString()`

is used to convert a `BigInt`

value to a string. It essentially wraps the `BigInt`

with a double or single quote while removing the trailing `n`

. It can be used like this:

const value1 = 35n; const sample1 = value1.toString() console.log(sample1) // Expected result: "35"

You can verify that the returned value is now a `string`

by using the `typeof`

as shown below:

const value1 = 35n; const sample1 = value1.toString() const valueDataType = typeof sample1 console.log(valueDataType) // Expected result: string

Optionally, you can also pass a `radix`

when converting a `BigInt`

to string. A `radix`

is actually the base you want to convert to. The `radix`

to be passed ranges from `2`

to `36`

. Also, when no `radix`

is passed, it defaults to `base 10`

. Hence, you can pass a `radix`

like this:

const value = 10n; const newValueBase16 = value.toString(16) console.log(newValueBase16); // Expected result: "a" const newValueBase5 = value.toString(5) console.log(newValueBase5) // Expected result: "20" const newValueBase10 = value.toString(10) console.log(newValueBase10) // Expected result: "10"

`BigInt.prototype.valueOf()`

The `valueOf`

method is used to get the primitive type of a `BigInt`

object. It demonstrated in the examples below:

const value = Object(7n) const valueOfDataType1 = typeof Object(7n) console.log(valueOfDataType1) // Expected result: object // You can use the .valueOf method to get the primitive type const valueOfDataType2 = typeof Object(7n) console.log(valueOfDataType2) // Expected result: bigint

`BigInt.prototype.toLocaleString()`

The `toLocaleString()`

method works similarly to the `toString()`

however, it can be used to return the string in a language-specific way or format. It accepts two parameters which are the `locale`

and the `options`

. It can be used like this:

const value = 23345689n; const valueAsFormattedString = value.toLocaleString('en-US') console.log(valueAsFormattedString) // Expected result: 23,345,689 const valueAsFormattedStringWithOptions = value.toLocaleString('en-US', { style: 'currency', currency: 'USD' }) console.log(valueAsFormattedStringWithOptions) // Expected result: $23,345,689.00

`BigInt.asIntN()`

The `BigInt.asIntN()`

is used to limit a `BigInt`

value within a set bit-`width`

while retaining the sign of the value. The syntax is as follows: `BigInt.asIntN(bitWidth, BigIntValue)`

. Therefore, it is suitable when we want to preserve the sign of a `BigInt`

value even when we want to constrain it.

Generally, the `bitWidth`

can be `8-bits`

, `16-bits`

, `32-bits`

, etc. So, when we set the width to be `8-bits`

, we are essentially saying that we want to accept a total of 256 `BigInt`

values that range between `-128`

to `128`

.

Note that it returns the value as it is if the value falls within the range of the accommodated values by the `bit`

and it returns the equivalent of the value in the `bit`

if it exceeds the range. It demonstrated in the examples below:

const bits = 8; const value1 = 126n; // Still in the range of -128 to 128 const value2 = 127n; // Still in the range of -128 to 128 const value3 = 128n; // Not in the range of -128 to 128 const value4 = 129n; // Not in the range of -128 to 128 const value5 = -67n; // Still the range of -128 to 128 const result1 = BigInt.asIntN(bits, value1) console.log(result1) // Expected result: 126n const result2 = BigInt.asIntN(bits, value2) console.log(result2) // Expected result: 127n const result3 = BigInt.asIntN(bits, value3) console.log(result3) // Expected result: -128n // -128n is the equivalent const result4 = BigInt.asIntN(bits, value4) console.log(result4) // Expected result: -127n // -127n is the equivalent const result5 = BigInt.asIntN(bits, value5) console.log(result5) // Expected result: -67n

`BigInt.asUintN()`

`BigInt.asUintN()`

performs similarly to `BigInt.asIntN()`

. The major difference is that it disregards the sign on the `BigInt`

value to be constrained, which only constrains between zero and the maximum quantity the bit can contain. For instance, `8-bits`

can contain 256 values; therefore, it will constrain between `0`

to `256`

with `0`

inclusive. It is demonstrated in the sample below:

const bits = 8; const value1 = 254n; // Still in the range of 0 to 256 const value2 = 255n; // Still in the range of 0 to 256 const value3 = 256n; // Not in the range of 0 to 256 const value4 = 257n; // Not in the range of 0 to 256 const value5 = 258n; // Not the range of 0 to 256 const result1 = BigInt.asUintN(bits, value1) console.log(result1) // Expected result: 254n const result2 = BigInt.asUintN(bits, value2) console.log(result2) // Expected result: 255n const result3 = BigInt.asUintN(bits, value3) console.log(result3) // Expected result: 0n // 0n is the equivalent const result4 = BigInt.asUintN(bits, value4) console.log(result4) // Expected result: 1n // 1n is the equivalent const result5 = BigInt.asUintN(bits, value5) console.log(result5) // Expected result: 2n // 2n is the equivalent

To determine the range of numbers a bit can contain, we can use this formula for signed integers when using `BigInt.asIntN()`

:

// For signed values - BigInt.asIntN() let bitWidth = n; let minimumRangeValue = -(2^(n-1)); // negative two to the power of n minus 1 let maximumRangeValue = (2^(n-1)) - 1; // two to the of n minus 1, minus 1 //let range = minimumRangeValue - maximumRangeValue // from -(2^(n-1)) to (2^(n-1)) - 1 // Therefore if n = 16; // 16 bits minimumRangeValue = -(2^(16-1)) = -(2^15) = -32768; maximumRangeValue = (2^(16-1)) - 1 = (2^15) - 1 = 32768 - 1 = 32767 // Range becomes -32768 to 32767 for signed 16 bits

Also, to determine the range of numbers a bit can contain, we can use this formula for unsigned integers when using `BigInt.asUintN()`

:

// For unsigned values - BigInt.asUintN() let bitWidth = n; let minimumRangeValue = 0; let maximumRangeValue = (2^n) - 1; // two the power of n, minus 1 //let range = minimumRangeValue - maximumRangeValue // 0 to (2^n) - 1 // Therefore if n = 16; // 16 bits minimumRangeValue = 0; maximumRangeValue = (2^16) - 1 = 65536 - 1 = 65535; // Range becomes 0 to 65535 for unsigned 16 bits

`BigInt`

conditionalsEssentially, the conditions that work with `numbers`

will also work with `BigInt`

. This means that `||`

, `&&`

, and `!`

will also work normally with `BigInt`

. Also, when we use `if`

statements, only the value `0n`

will evaluate to `false`

while the other whole `positive`

:

if(0n){ console.log('It is in if') }else{ console.log("it is in else") } // Expected Result: it is in else if(-2n){ console.log("it is in if") }else{ console.log("it is in else") } // Expected result: it is in if if(67n){ console.log("it is in if") }else{ console.log("it is in else") } // Expected result: it is in if

Itâ€™s worth saying that even the `Boolean`

function will also work as expected. This means that only `0n`

will evaluate to `false`

, as shown below:

const isValueNone = Boolean(0n) console.log(isValueNone) // Expected result: false const isValueExists1 = Boolean(79n) console.log(isValueExists1) // Expected result: true const isValueExists2 = Boolean(-65n) console.log(isValueExists2) // Expected result: true

`BigInt`

common rules and precautionsTo use `BigInt`

without seeing errors, there are rules we need to follow. Hereâ€™s the full list:

- Do not use
`new`

keyword when creating`BigInt`

- Do not use decimals with
`Bigint`

- Errors with
`BigInt`

coercion - Do not use
`JSON.stringify`

directly with`BigInt`

- Do not use the positive unary operation with
`BigInt`

- Limited built-in methods
- Division operations with
`BigInt`

returns a truncated result

First, do not use `new`

keyword when creating `BigInt`

. In JavaScript, we often use the `new`

keyword to initialize a prototype of a class. However, that is not the case with `BigInt`

as it will throw a `TypeError`

when we use the `new`

keyword:

// Do not do this - const value = new BigInt(54); // Expected result: Throws TypeError

Also, do not use decimals with `Bigint`

. When we try to convert a decimal `number`

to a `BigInt`

value, it will throw a `RangeError`

. Similarly, we will get a `SyntaxError`

when using an implicitly converted `BigInt`

with a decimal point:

// Do not do this: const value = 7.8n + 9n; // Expected result: Throws a SyntaxError

Additionally, you may run into errors with `BigInt`

coercion, such as mixing `numbers`

with `BigInt`

will throw a `TypeError`

:

// Do not do this: const result = 2n + 4; console.log(result) // Expected result: TypeError: cannot convert BigInt to number

And, using `null`

, `undefined`

, and `Symbol`

with `BigInt`

will throw an error:

console.log(BigInt(null)) // Expected result: TypeError: can't convert null to BigInt console.log(BigInt(undefined)) // Expected result: TypeError: can't convert undefined to BigInt console.log(BigInt(Symbol())) // Expected result: TypeError: can't convert Symbol() to BigInt

To avoid more errors, do not use `JSON.stringify`

directly with `BigInt`

. We cannot directly use the `JSON.stringify`

direct on `BigInt`

will throw a `TypeError`

:

const theStringified = JSON.stringify(5n) console.log(theStringified) // Expected result: TypeError: BigInt value can't be serialized in JSON

Instead, we can `stringify`

our `BigInt`

value to implement out `toJSON`

method or by using a replacer. To implement the `toJSON`

method, we simply need to add the following piece of code to our program file before using the `JSON.stringify()`

method:

BigInt.prototype.toJSON = function () { return this.toString(); };

To implement `JSON.stringify()`

method using the replacer method, we simply have to add the following piece of code to our program:

const replacer = (key, value) => { return typeof value === "bigint" ? value.toString() : value }

We can implement `JSON.stringify()`

using any of the two methods like this:

// Method 1 - // Implementing toJSON // Add this before you inplement JSON.stringify() BigInt.prototype.toJSON = function () { return this.toString(); }; const value1 = {one: 5n, two: 667n}; const value1ToJson = JSON.stringify(value1); console.log(value1ToJson); // Expected result: {"one":"5","two":"667"} // Method 2 - // Implement with a replacer const replacer = (key, value) => { return typeof value === "bigint" ? value.toString() : value } const value2 = {seven: 7n, twenty: 20n, five: 5n}; const value2ToJson = JSON.stringify(value2, replacer) console.log(value2ToJson) // Expected result: {"seven":"7","twenty":"20","five":"5"}

Normally, the negative unary operation will always work with `BigInt`

values. However, the positive will break the `asm.js`

, which expects `+value`

to always return a positive number or throw an error. So, donâ€™t use the positive unary operation with `BigInt`

because doing something like this `+2n`

will throw a `TypeError`

:

console.log(+2n) // Expected result: TypeError: can't convert BigInt to number

`BigInt`

One of the limitations of `BigInt`

is that we cannot use built-in `Math`

functions in JavaScript. Instead, we are expected to construct our mathematic functions ourselves. Hence, performing operations such as `Math.sqrt()`

will throw a `TypeError`

exception:

console.log(Math.sqrt(4n)) // Expected result: TypeError: can't convert BigInt to number

Additionally, division operations with `BigInt`

returns a truncated result. It has been established that `BigInt`

values cannot be a decimal. This fact is extended to the division operation, which can sometimes return a decimal dividend, such as in `7 / 4 = 1.75`

. Hence, because of this, `BigInt`

values will always be rounded to 0. That means that `7n / 4n = 1n`

and not `1.75n`

, as expected.

Any version of Node.js from v10.4, including newer versions such as v18.16, has support for `BigInt`

. This means that the older version will throw a `SyntaxError`

. Similarly, most modern browsers support `BigInt`

. You can see the total supported browsers on caniuse.

At the time of writing, more than 94 percent of browsers support the `BigInt`

constructor and its methods.

In this article, we learned about `BigInts`

, their available methods, use cases, and the challenges of working with them. However, we should know that numbers are also great for day-to-day programs such as small websites and that we should use `BigInt`

s only when we are sure we will be working with large sums.

Thanks for reading. I hope you enjoyed this article, and be sure to leave a comment if you have any questions. Happy coding!

Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.

LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

Would you be interested in joining LogRocket's developer community?

Join LogRocketâ€™s Content Advisory Board. Youâ€™ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up nowSimplify component interaction and dynamic theming in Vue 3 with defineExpose and for better control and flexibility.

Explore how to integrate TypeScript into a Node.js and Express application, leveraging ts-node, nodemon, and TypeScript path aliases.

es-toolkit is a lightweight, efficient JavaScript utility library, ideal as a modern Lodash alternative for smaller bundles.

The use cases for the ResizeObserver API may not be immediately obvious, so letâ€™s take a look at a few practical examples.