BemÀstra JavaScripts BigInt för precisa berÀkningar med stora heltal. Utforska syntax, anvÀndningsfall och vanliga fallgropar som JSON-serialisering.
JavaScript BigInt: En omfattande guide till berÀkningar med stora tal
Under mĂ„nga Ă„r stod JavaScript-utvecklare inför en tyst men betydande begrĂ€nsning: sprĂ„kets inbyggda förmĂ„ga att hantera tal. Ăven om JavaScripts Number
-typ Àr perfekt för vardagliga berÀkningar, fallerade den nÀr den konfronterades med de verkligt massiva heltal som krÀvs inom omrÄden som kryptografi, vetenskapliga berÀkningar och moderna datasystem. Detta ledde till en vÀrld av nödlösningar, tredjepartsbibliotek och subtila precisionsfel som var svÄra att felsöka.
Den eran Àr över. Introduktionen av BigInt som en inbyggd primitiv typ i JavaScript har revolutionerat hur vi arbetar med stora tal. Det ger ett robust, ergonomiskt och effektivt sÀtt att utföra heltalsaritmetik med godtycklig precision, direkt i sprÄket.
Denna omfattande guide Àr för utvecklare över hela vÀrlden. Vi kommer att dyka djupt ner i "varför, vad och hur" gÀllande BigInt. Oavsett om du bygger en finansiell applikation, interagerar med en blockkedja eller bara försöker förstÄ varför ditt stora unika ID frÄn ett API beter sig konstigt, kommer den hÀr artikeln att ge dig kunskapen för att bemÀstra BigInt.
Problemet: GrÀnserna för JavaScripts Number-typ
Innan vi kan uppskatta lösningen mÄste vi helt förstÄ problemet. Under större delen av sin historia har JavaScript bara haft en tal-typ: Number
-typen. Under huven representeras den som ett IEEE 754 64-bitars flyttal med dubbel precision. Detta format Àr utmÀrkt för att representera ett brett spektrum av vÀrden, inklusive decimaltal, men det har en kritisk begrÀnsning nÀr det gÀller heltal.
Möt MAX_SAFE_INTEGER
PÄ grund av dess flyttalsrepresentation finns det en grÀns för storleken pÄ ett heltal som kan representeras med perfekt precision. Denna grÀns exponeras via en konstant: Number.MAX_SAFE_INTEGER
.
Dess vÀrde Àr 253 - 1, vilket Àr 9 007 199 254 740 991. LÄt oss för enkelhetens skull kalla det nio biljarder.
Alla heltal inom intervallet -Number.MAX_SAFE_INTEGER
till +Number.MAX_SAFE_INTEGER
anses vara ett "sÀkert heltal". Det innebÀr att det kan representeras exakt och jÀmföras korrekt. Men vad hÀnder nÀr vi gÄr utanför detta intervall?
LÄt oss se det i praktiken:
const maxSafe = Number.MAX_SAFE_INTEGER;
console.log(maxSafe); // 9007199254740991
// LÄt oss addera 1 till det
console.log(maxSafe + 1); // 9007199254740992 - Detta ser korrekt ut
// LÄt oss addera ytterligare 1
console.log(maxSafe + 2); // 9007199254740992 - Oj dÄ. Felaktigt resultat.
// Det blir vÀrre
console.log(maxSafe + 3); // 9007199254740994 - VĂ€nta, va?
console.log(maxSafe + 4); // 9007199254740996 - Den hoppar över tal!
// JÀmlikhetskontroll misslyckas ocksÄ
console.log(maxSafe + 1 === maxSafe + 2); // true - Det hÀr Àr matematiskt fel!
Som du kan se, sÄ fort vi överskrider Number.MAX_SAFE_INTEGER
, kan JavaScript inte lÀngre garantera precisionen i vÄra berÀkningar. Talrepresentationen börjar fÄ luckor, vilket leder till avrundningsfel och felaktiga resultat. Detta Àr en mardröm för applikationer som krÀver noggrannhet med stora heltal.
De gamla nödlösningarna
Under mÄnga Är förlitade sig den globala utvecklargemenskapen pÄ externa bibliotek för att lösa detta problem. Bibliotek som bignumber.js
, decimal.js
och long.js
blev standardverktyg. De fungerade genom att representera stora tal som strÀngar eller arrayer av siffror och implementera aritmetiska operationer i mjukvara.
Ăven om de var effektiva, kom dessa bibliotek med kompromisser:
- Prestanda-overhead: Operationerna var betydligt lÄngsammare Àn inbyggda talberÀkningar.
- Paketstorlek: De lade till vikt i applikationspaketen, ett bekymmer för webbprestanda.
- Annorlunda syntax: Utvecklare var tvungna att anvÀnda objektmetoder (t.ex.
a.add(b)
) istÀllet för vanliga aritmetiska operatorer (a + b
), vilket gjorde koden mindre intuitiv.
Introduktion till BigInt: Den inbyggda lösningen
BigInt introducerades i ES2020 för att lösa detta problem inbyggt. En BigInt
Àr en ny primitiv typ i JavaScript som ger ett sÀtt att representera heltal större Àn 253 - 1.
Nyckelfunktionen hos BigInt Àr att dess storlek inte Àr fast. Det kan representera godtyckligt stora heltal, begrÀnsat endast av det tillgÀngliga minnet i vÀrdsystemet. Detta eliminerar helt de precisionsproblem vi sÄg med Number
-typen.
Hur man skapar en BigInt
Det finns tvÄ huvudsakliga sÀtt att skapa en BigInt:
- LÀgga till `n` efter en heltalsliteral: Detta Àr den enklaste och vanligaste metoden.
- AnvÀnda konstruktorfunktionen `BigInt()`: Detta Àr anvÀndbart nÀr man konverterar ett vÀrde frÄn en annan typ, som en strÀng eller ett tal.
SÄ hÀr ser de ut i kod:
// 1. AnvÀnda suffixet 'n'
const myFirstBigInt = 900719925474099199n;
const anotherBigInt = 123456789012345678901234567890n;
// 2. AnvÀnda konstruktorn BigInt()
const fromString = BigInt("98765432109876543210");
const fromNumber = BigInt(100);
// Du kan kontrollera typen
console.log(typeof myFirstBigInt); // "bigint"
console.log(typeof 100); // "number"
Med BigInt fungerar vÄr tidigare misslyckade berÀkning nu perfekt:
const maxSafePlusOne = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
const maxSafePlusTwo = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
console.log(maxSafePlusOne.toString()); // "9007199254740992"
console.log(maxSafePlusTwo.toString()); // "9007199254740993"
// JÀmlikhet fungerar som förvÀntat
console.log(maxSafePlusOne === maxSafePlusTwo); // false
Att arbeta med BigInt: Syntax och operationer
BigInts beter sig i stort sett som vanliga tal, men med nÄgra avgörande skillnader som varje utvecklare mÄste förstÄ för att undvika buggar.
Aritmetiska operationer
Alla standardaritmetiska operatorer fungerar med BigInts:
- Addition:
+
- Subtraktion:
-
- Multiplikation:
*
- Exponentiering:
**
- Modulus (Rest):
%
Den enda operatorn som beter sig annorlunda Àr division (`/`).
const a = 10n;
const b = 3n;
console.log(a + b); // 13n
console.log(a - b); // 7n
console.log(a * b); // 30n
console.log(a ** b); // 1000n
console.log(a % b); // 1n
FörbehÄllet med division
Eftersom BigInts bara kan representera heltal, trunkeras resultatet av en division alltid (decimaldelen tas bort). Det avrundas inte.
const a = 10n;
const b = 3n;
console.log(a / b); // 3n (inte 3.333...n)
const c = 9n;
const d = 10n;
console.log(c / d); // 0n
Detta Àr en kritisk skillnad. Om du behöver utföra berÀkningar med decimaler Àr BigInt inte rÀtt verktyg. Du skulle behöva fortsÀtta anvÀnda Number
eller ett dedikerat decimalbibliotek.
JÀmförelse och jÀmlikhet
JÀmförelseoperatorer som >
, <
, >=
och <=
fungerar sömlöst mellan BigInts, och Àven mellan en BigInt och en Number.
console.log(10n > 5); // true
console.log(10n < 20); // true
console.log(10n > 20n); // false
JÀmlikhet Àr dock mer nyanserad och Àr en vanlig kÀlla till förvirring.
- Lös jÀmlikhet (`==`): Denna operator utför typkonvertering. Den anser att en BigInt och en Number med samma matematiska vÀrde Àr lika.
- Strikt jÀmlikhet (`===`): Denna operator utför inte typkonvertering. Eftersom BigInt och Number Àr olika typer kommer den alltid att returnera
false
nÀr de jÀmförs.
console.log(10n == 10); // true - Var försiktig med detta!
console.log(10n === 10); // false - Rekommenderas för tydlighetens skull.
console.log(0n == 0); // true
console.log(0n === 0); // false
BÀsta praxis: För att undvika subtila buggar, anvÀnd alltid strikt jÀmlikhet (`===`) och var explicit med de typer du jÀmför. Om du behöver jÀmföra en BigInt och en Number Àr det ofta tydligare att först konvertera den ena till den andra, med hÀnsyn till potentiell precisionsförlust.
Typkonflikten: En strikt separation
JavaScript upprÀtthÄller en strikt regel: du kan inte blanda BigInt- och Number-operander i de flesta aritmetiska operationer.
Försök att göra det kommer att resultera i ett TypeError
. Detta Àr ett medvetet designval för att förhindra att utvecklare oavsiktligt förlorar precision.
const myBigInt = 100n;
const myNumber = 50;
try {
const result = myBigInt + myNumber; // Detta kommer att kasta ett fel
} catch (error) {
console.log(error); // TypeError: Cannot mix BigInt and other types, use explicit conversions
}
Den korrekta metoden: Explicita konverteringar
För att utföra en operation mellan en BigInt och en Number mÄste du explicit konvertera en av dem.
const myBigInt = 100n;
const myNumber = 50;
// Konvertera Number till BigInt (sÀkert)
const result1 = myBigInt + BigInt(myNumber);
console.log(result1); // 150n
// Konvertera BigInt till Number (potentiellt osÀkert!)
const veryLargeBigInt = 900719925474099199n;
// Detta kommer att förlora precision!
const unsafeNumber = Number(veryLargeBigInt);
console.log(unsafeNumber); // 900719925474099200 - VĂ€rdet har avrundats!
const safeResult = Number(100n) + myNumber;
console.log(safeResult); // 150
Kritisk regel: Konvertera endast en BigInt till en Number om du Àr helt sÀker pÄ att den passar inom det sÀkra heltalsintervallet. Annars, konvertera alltid Number till en BigInt för att bibehÄlla precisionen.
Praktiska anvÀndningsfall för BigInt i en global kontext
Behovet av BigInt Àr inte ett abstrakt akademiskt problem. Det löser verkliga utmaningar som utvecklare stÄr inför i olika internationella domÀner.
1. HögprecisionstidsstÀmplar
JavaScript's `Date.now()` returnerar antalet millisekunder sedan Unix-epoken. Ăven om det Ă€r tillrĂ€ckligt för mĂ„nga webbapplikationer, Ă€r det inte tillrĂ€ckligt granulĂ€rt för högpresterande system. MĂ„nga distribuerade system, databaser och loggningsramverk över hela vĂ€rlden anvĂ€nder nanosekund-precisionstidsstĂ€mplar för att korrekt ordna hĂ€ndelser. Dessa tidsstĂ€mplar representeras ofta som 64-bitars heltal, vilket Ă€r för stort för Number
-typen.
// En tidsstÀmpel frÄn ett högupplöst system (t.ex. i nanosekunder)
const nanoTimestampStr = "1670000000123456789";
// Att anvÀnda Number resulterar i precisionsförlust
const lostPrecision = Number(nanoTimestampStr);
console.log(lostPrecision); // 1670000000123456800 - Felaktigt!
// Med BigInt bevaras den perfekt
const correctTimestamp = BigInt(nanoTimestampStr);
console.log(correctTimestamp.toString()); // "1670000000123456789"
// Nu kan vi utföra exakta berÀkningar
const oneSecondInNanos = 1_000_000_000n;
const nextSecond = correctTimestamp + oneSecondInNanos;
console.log(nextSecond.toString()); // "1670001000123456789"
2. Unika identifierare (ID:n) frÄn API:er
Ett mycket vanligt scenario Àr att interagera med API:er som anvÀnder 64-bitars heltal för unika objekt-ID:n. Detta Àr ett mönster som anvÀnds av stora globala plattformar som Twitter (Snowflake IDs) och mÄnga databassystem (t.ex. BIGINT
-typen i SQL).
NÀr du hÀmtar data frÄn ett sÄdant API kan JSON-parsern i din webblÀsare eller Node.js-miljö försöka tolka detta stora ID som en Number
, vilket leder till datakorruption innan du ens har en chans att arbeta med det.
// Ett typiskt JSON-svar frÄn ett API
// Notera: ID:t Àr ett stort tal, inte en strÀng.
const jsonResponse = '{"id": 1367874743838343168, "text": "Hello, world!"}';
// Standard JSON.parse kommer att korrumpera ID:t
const parsedData = JSON.parse(jsonResponse);
console.log(parsedData.id); // 1367874743838343200 - Fel ID!
// Lösning: Se till att API:et skickar stora ID:n som strÀngar.
const safeJsonResponse = '{"id": "1367874743838343168", "text": "Hello, world!"}';
const safeParsedData = JSON.parse(safeJsonResponse);
const userId = BigInt(safeParsedData.id);
console.log(userId); // 1367874743838343168n - Korrekt!
Detta Àr anledningen till att det Àr en allmÀnt accepterad bÀsta praxis för API:er vÀrlden över att serialisera stora heltals-ID:n som strÀngar i JSON-nyttolaster för att sÀkerstÀlla kompatibilitet med alla klienter.
3. Kryptografi
Modern kryptografi bygger pÄ matematik som involverar extremt stora heltal. Algoritmer som RSA förlitar sig pÄ operationer med tal som Àr hundratals eller till och med tusentals bitar lÄnga. BigInt gör det möjligt att utföra dessa berÀkningar inbyggt i JavaScript, vilket Àr avgörande för webbaserade kryptografiska applikationer, som de som anvÀnder Web Crypto API eller implementerar protokoll i Node.js.
Ăven om ett fullstĂ€ndigt kryptografiskt exempel Ă€r komplicerat, kan vi se en konceptuell demonstration:
// TvÄ vÀldigt stora primtal (endast i demonstrationssyfte)
const p = 1143400375533529n;
const q = 982451653n; // Ett mindre för exemplets skull
// I RSA multiplicerar du dem för att fÄ modulus
const n = p * q;
console.log(n.toString()); // "1123281328905333100311297"
// Denna berÀkning skulle vara omöjlig med typen Number.
// BigInt hanterar det utan problem.
4. Finansiella och blockkedjeapplikationer
NÀr man hanterar finans, sÀrskilt i samband med kryptovalutor, Àr precision av yttersta vikt. MÄnga kryptovalutor, som Bitcoin, mÀter vÀrde i sin minsta enhet (t.ex. satoshis). Det totala utbudet av dessa enheter kan lÀtt överskrida Number.MAX_SAFE_INTEGER
. BigInt Àr det perfekta verktyget för att hantera dessa stora, exakta kvantiteter utan att tillgripa flyttalsaritmetik, som Àr benÀgen för avrundningsfel.
// 1 Bitcoin = 100 000 000 satoshis
const satoshisPerBTC = 100_000_000n;
// Totala utbudet av Bitcoin Àr 21 miljoner
const totalBTCSupply = 21_000_000n;
// BerÀkna totalt antal satoshis
const totalSatoshis = totalBTCSupply * satoshisPerBTC;
// 2 100 000 000 000 000 - Detta Àr 2,1 biljarder
console.log(totalSatoshis.toString());
// Detta vÀrde Àr större Àn Number.MAX_SAFE_INTEGER
console.log(totalSatoshis > BigInt(Number.MAX_SAFE_INTEGER)); // true
Avancerade Àmnen och vanliga fallgropar
Serialisering och JSON.stringify()
Ett av de vanligaste problemen utvecklare stöter pÄ Àr att serialisera objekt som innehÄller BigInts. Som standard vet JSON.stringify()
inte hur man hanterar typen bigint
och kommer att kasta ett TypeError
.
const data = {
id: 12345678901234567890n,
user: 'alex'
};
try {
JSON.stringify(data);
} catch (error) {
console.log(error); // TypeError: Do not know how to serialize a BigInt
}
Lösning 1: Implementera en `toJSON`-metod
Du kan berÀtta för `JSON.stringify` hur man hanterar BigInts genom att lÀgga till en `toJSON`-metod i `BigInt.prototype`. Detta tillvÀgagÄngssÀtt patchar den globala prototypen, vilket kan vara oönskat i vissa delade miljöer, men det Àr mycket effektivt.
// En global patch. AnvÀnd med eftertanke.
BigInt.prototype.toJSON = function() {
return this.toString();
};
const data = { id: 12345678901234567890n, user: 'alex' };
const jsonString = JSON.stringify(data);
console.log(jsonString); // '{"id":"12345678901234567890","user":"alex"}'
Lösning 2: AnvÀnd en replacer-funktion
Ett sÀkrare, mer lokaliserat tillvÀgagÄngssÀtt Àr att anvÀnda `replacer`-argumentet i `JSON.stringify`. Denna funktion anropas för varje nyckel/vÀrde-par och lÄter dig omvandla vÀrdet före serialisering.
const data = { id: 12345678901234567890n, user: 'alex' };
const replacer = (key, value) => {
if (typeof value === 'bigint') {
return value.toString();
}
return value;
};
const jsonString = JSON.stringify(data, replacer);
console.log(jsonString); // '{"id":"12345678901234567890","user":"alex"}'
Bitvisa operationer
BigInt stöder alla bitvisa operatorer som du Àr bekant med frÄn Number
-typen: `&` (AND), `|` (OR), `^` (XOR), `~` (NOT), `<<` (vÀnsterskift) och `>>` (tecken-propagerande högerskift). Dessa Àr sÀrskilt anvÀndbara nÀr man arbetar med lÄgnivÄ-dataformat, behörigheter eller vissa typer av algoritmer.
const permissions = 5n; // 0101 binÀrt
const READ_PERMISSION = 4n; // 0100
const WRITE_PERMISSION = 2n; // 0010
// Kontrollera om lÀsbehörighet Àr satt
console.log((permissions & READ_PERMISSION) > 0n); // true
// Kontrollera om skrivbehörighet Àr satt
console.log((permissions & WRITE_PERMISSION) > 0n); // false
// LÀgg till skrivbehörighet
const newPermissions = permissions | WRITE_PERMISSION;
console.log(newPermissions); // 7n (vilket Àr 0111)
PrestandaövervÀganden
Ăven om BigInt Ă€r otroligt kraftfullt Ă€r det viktigt att förstĂ„ dess prestandaegenskaper:
- Number vs. BigInt: För heltal inom det sÀkra intervallet Àr standardoperationer med
Number
betydligt snabbare. Detta beror pÄ att de ofta kan mappas direkt till instruktioner pÄ maskinnivÄ som bearbetas av datorns CPU. BigInt-operationer, som Àr av godtycklig storlek, krÀver mer komplexa mjukvarubaserade algoritmer. - BigInt vs. Bibliotek: Inbyggt
BigInt
Àr generellt sett mycket snabbare Àn JavaScript-baserade bibliotek för stora tal. Implementeringen Àr en del av JavaScript-motorn (som V8 eller SpiderMonkey) och Àr skriven i ett lÀgre nivÄsprÄk som C++, vilket ger den en betydande prestandafördel.
Den gyllene regeln: AnvÀnd Number
för alla numeriska berÀkningar om du inte har en specifik anledning att tro att vÀrdena kan överskrida Number.MAX_SAFE_INTEGER
. AnvÀnd BigInt
nÀr du behöver dess kapacitet, inte som en standardersÀttning för alla tal.
Kompatibilitet med webblÀsare och miljöer
BigInt Àr en modern JavaScript-funktion, men dess stöd Àr nu utbrett över det globala ekosystemet.
- WebblÀsare: Stöds i alla större moderna webblÀsare (Chrome 67+, Firefox 68+, Safari 14+, Edge 79+).
- Node.js: Stöds sedan version 10.4.0.
För projekt som behöver stödja mycket gamla miljöer kan transpileriing med verktyg som Babel vara ett alternativ, men detta medför en prestandaförlust. Med tanke pÄ det breda stödet idag kan de flesta nya projekt anvÀnda BigInt inbyggt utan problem.
Slutsats och bÀsta praxis
BigInt Àr ett kraftfullt och vÀsentligt tillÀgg till JavaScript-sprÄket. Det ger en inbyggd, effektiv och ergonomisk lösning pÄ det lÄngvariga problemet med aritmetik för stora heltal, vilket möjliggör en ny klass av applikationer som kan byggas med JavaScript, frÄn kryptografi till högprecisionsdatahantering.
För att anvÀnda det effektivt och undvika vanliga fallgropar, ha dessa bÀsta praxis i Ätanke:
- AnvÀnd `n`-suffixet: Föredra literalsyntaxen `123n` för att skapa BigInts. Det Àr tydligt, koncist och undviker potentiell precisionsförlust vid skapandet.
- Blanda inte typer: Kom ihÄg att du inte kan blanda BigInt och Number i aritmetiska operationer. Var explicit med dina konverteringar: `BigInt()` eller `Number()`.
- Prioritera precision: NÀr du konverterar mellan typer, föredra alltid att konvertera en `Number` till en `BigInt` för att förhindra oavsiktlig precisionsförlust.
- AnvÀnd strikt jÀmlikhet: AnvÀnd `===` istÀllet för `==` för jÀmförelser för att undvika förvirrande beteende orsakat av typkonvertering.
- Hantera JSON-serialisering: Planera för serialisering av BigInts. AnvÀnd en anpassad `replacer`-funktion i `JSON.stringify` för en sÀker, icke-global lösning.
- VÀlj rÀtt verktyg: AnvÀnd `Number` för allmÀn matematik inom det sÀkra heltalsintervallet för bÀttre prestanda. AnvÀnd endast `BigInt` nÀr du verkligen behöver dess godtyckliga precisionskapacitet.
Genom att omfamna BigInt och förstÄ dess regler kan du skriva mer robusta, exakta och kraftfulla JavaScript-applikationer som kan hantera numeriska utmaningar av alla skalor.