Apgūstiet JavaScript BigInt precīziem, liela mēroga veselo skaitļu aprēķiniem. Izpētiet sintaksi, lietošanas gadījumus kriptogrāfijā un finansēs, un pārvariet tādas problēmas kā JSON serializācija.
JavaScript BigInt: Visaptverošs ceļvedis lielo skaitļu aprēķiniem
Daudzus gadus JavaScript izstrādātāji saskārās ar klusu, bet būtisku ierobežojumu: valodas dabisko spēju apstrādāt skaitļus. Lai gan JavaScript Number
tips bija lieliski piemērots ikdienas aprēķiniem, tas kļuva neefektīvs, saskaroties ar patiesi milzīgiem veseliem skaitļiem, kas nepieciešami tādās jomās kā kriptogrāfija, zinātniskie aprēķini un mūsdienu datu sistēmas. Tas noveda pie dažādiem risinājumiem, trešo pušu bibliotēkām un smalkām, grūti atkļūdojamām precizitātes kļūdām.
Šī ēra ir beigusies. BigInt ieviešana kā dabisks JavaScript primitīvais tips ir revolucionizējis veidu, kā mēs strādājam ar lieliem skaitļiem. Tas nodrošina stabilu, ergonomisku un efektīvu veidu, kā veikt patvaļīgas precizitātes veselo skaitļu aritmētiku tieši valodas ietvaros.
Šis visaptverošais ceļvedis ir paredzēts izstrādātājiem visā pasaulē. Mēs iedziļināsimies BigInt "kāpēc, kas un kā". Neatkarīgi no tā, vai jūs veidojat finanšu lietojumprogrammu, mijiedarbojaties ar blokķēdi vai vienkārši mēģināt saprast, kāpēc jūsu lielais unikālais ID no API uzvedas dīvaini, šis raksts sniegs jums zināšanas, lai apgūtu BigInt.
Problēma: JavaScript Number tipa robežas
Pirms mēs varam novērtēt risinājumu, mums ir pilnībā jāsaprot problēma. Lielāko daļu savas vēstures JavaScript ir bijis tikai viens skaitļu tips: Number
tips. Zem pārsega tas tiek attēlots kā IEEE 754 dubultās precizitātes 64 bitu peldošā punkta skaitlis. Šis formāts ir lielisks, lai attēlotu plašu vērtību diapazonu, ieskaitot decimāldaļas, bet tam ir būtisks ierobežojums, runājot par veseliem skaitļiem.
Iepazīstieties ar MAX_SAFE_INTEGER
Tā peldošā punkta attēlojuma dēļ pastāv ierobežojums veselā skaitļa lielumam, ko var attēlot ar perfektu precizitāti. Šis ierobežojums ir pieejams caur konstanti: Number.MAX_SAFE_INTEGER
.
Tās vērtība ir 253 - 1, kas ir 9,007,199,254,740,991. Īsumā sauksim to par deviņiem kvadriljoniem.
Jebkurš vesels skaitlis diapazonā no -Number.MAX_SAFE_INTEGER
līdz +Number.MAX_SAFE_INTEGER
tiek uzskatīts par "drošu veselo skaitli". Tas nozīmē, ka to var precīzi attēlot un pareizi salīdzināt. Bet kas notiek, kad mēs izkāpjam ārpus šī diapazona?
Apskatīsim to darbībā:
const maxSafe = Number.MAX_SAFE_INTEGER;
console.log(maxSafe); // 9007199254740991
// Let's add 1 to it
console.log(maxSafe + 1); // 9007199254740992 - This looks correct
// Let's add another 1
console.log(maxSafe + 2); // 9007199254740992 - Uh oh. Incorrect result.
// It gets worse
console.log(maxSafe + 3); // 9007199254740994 - Wait, what?
console.log(maxSafe + 4); // 9007199254740996 - It's skipping numbers!
// Checking for equality also fails
console.log(maxSafe + 1 === maxSafe + 2); // true - This is mathematically wrong!
Kā redzams, tiklīdz mēs pārsniedzam Number.MAX_SAFE_INTEGER
, JavaScript vairs nevar garantēt mūsu aprēķinu precizitāti. Skaitļu attēlojumā sāk parādīties nepilnības, kas noved pie noapaļošanas kļūdām un nepareiziem rezultātiem. Tas ir murgs lietojumprogrammām, kas prasa precizitāti ar lieliem veseliem skaitļiem.
Vecie risinājumi
Gadiem ilgi globālā izstrādātāju kopiena paļāvās uz ārējām bibliotēkām, lai atrisinātu šo problēmu. Bibliotēkas kā bignumber.js
, decimal.js
un long.js
kļuva par standarta rīkiem. Tās darbojās, attēlojot lielus skaitļus kā virknes vai ciparu masīvus un implementējot aritmētiskās operācijas programmatūrā.
Lai gan šīs bibliotēkas bija efektīvas, tām bija savi kompromisi:
- Veiktspējas slogs: Operācijas bija ievērojami lēnākas nekā dabiskie skaitļu aprēķini.
- Pakotnes izmērs: Tās palielināja lietojumprogrammu pakotņu svaru, kas ir svarīgi tīmekļa veiktspējai.
- Atšķirīga sintakse: Izstrādātājiem bija jāizmanto objektu metodes (piem.,
a.add(b)
), nevis standarta aritmētiskie operatori (a + b
), padarot kodu mazāk intuitīvu.
Iepazīstinām ar BigInt: Dabiskais risinājums
BigInt tika ieviests ES2020, lai dabiski atrisinātu šo problēmu. BigInt
ir jauns primitīvais tips JavaScript, kas nodrošina veidu, kā attēlot veselus skaitļus, kas lielāki par 253 - 1.
BigInt galvenā iezīme ir tā, ka tā izmērs nav fiksēts. Tas var attēlot patvaļīgi lielus veselus skaitļus, ko ierobežo tikai pieejamā atmiņa resursdatora sistēmā. Tas pilnībā novērš precizitātes problēmas, kuras mēs redzējām ar Number
tipu.
Kā izveidot BigInt
Ir divi galvenie veidi, kā izveidot BigInt:
- Pievienojot `n` vesela skaitļa literālim: Šī ir vienkāršākā un visbiežāk izmantotā metode.
- Izmantojot `BigInt()` konstruktora funkciju: Tas ir noderīgi, konvertējot vērtību no cita tipa, piemēram, virknes vai skaitļa.
Lūk, kā tas izskatās kodā:
// 1. Using the 'n' suffix
const myFirstBigInt = 900719925474099199n;
const anotherBigInt = 123456789012345678901234567890n;
// 2. Using the BigInt() constructor
const fromString = BigInt("98765432109876543210");
const fromNumber = BigInt(100);
// You can check the type
console.log(typeof myFirstBigInt); // "bigint"
console.log(typeof 100); // "number"
Ar BigInt mūsu iepriekšējais neveiksmīgais aprēķins tagad darbojas perfekti:
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"
// Equality works as expected
console.log(maxSafePlusOne === maxSafePlusTwo); // false
Darbs ar BigInt: Sintakse un operācijas
BigInt uzvedas ļoti līdzīgi parastajiem skaitļiem, bet ar dažām būtiskām atšķirībām, kuras katram izstrādātājam ir jāsaprot, lai izvairītos no kļūdām.
Aritmētiskās operācijas
Visi standarta aritmētiskie operatori darbojas ar BigInt:
- Saskaitīšana:
+
- Atņemšana:
-
- Reizināšana:
*
- Kāpināšana:
**
- Modulis (atlikums):
%
Vienīgais operators, kas uzvedas atšķirīgi, ir dalīšana (`/`).
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
Dalīšanas īpatnība
Tā kā BigInt var attēlot tikai veselus skaitļus, dalīšanas rezultāts vienmēr tiek saīsināts (daļskaitļa daļa tiek atmesta). Tas netiek noapaļots.
const a = 10n;
const b = 3n;
console.log(a / b); // 3n (not 3.333...n)
const c = 9n;
const d = 10n;
console.log(c / d); // 0n
Šī ir kritiska atšķirība. Ja jums ir nepieciešams veikt aprēķinus ar decimāldaļām, BigInt nav pareizais rīks. Jums būtu jāturpina izmantot Number
vai specializētu decimāldaļu bibliotēku.
Salīdzināšana un vienādība
Salīdzināšanas operatori, piemēram, >
, <
, >=
un <=
, darbojas nevainojami starp BigInt, un pat starp BigInt un Number.
console.log(10n > 5); // true
console.log(10n < 20); // true
console.log(10n > 20n); // false
Tomēr vienādība ir niansētāka un ir biežs neskaidrību avots.
- Brīvā vienādība (`==`): Šis operators veic tipu piespiešanu. Tas uzskata BigInt un Number ar vienādu matemātisko vērtību par vienādiem.
- Stingrā vienādība (`===`): Šis operators neveic tipu piespiešanu. Tā kā BigInt un Number ir dažādi tipi, tas vienmēr atgriezīs
false
, tos salīdzinot.
console.log(10n == 10); // true - Be careful with this!
console.log(10n === 10); // false - Recommended for clarity.
console.log(0n == 0); // true
console.log(0n === 0); // false
Labākā prakse: Lai izvairītos no smalkām kļūdām, vienmēr izmantojiet stingro vienādību (`===`) un esiet skaidri par tipiem, kurus salīdzināt. Ja jums ir nepieciešams salīdzināt BigInt un Number, bieži vien ir skaidrāk vispirms konvertēt vienu uz otru, paturot prātā iespējamo precizitātes zudumu.
Tipu neatbilstība: Stingra nošķiršana
JavaScript piemēro stingru noteikumu: jūs nevarat sajaukt BigInt un Number operandus lielākajā daļā aritmētisko operāciju.
Mēģinot to darīt, tiks izraisīta TypeError
. Šī ir apzināta dizaina izvēle, lai novērstu to, ka izstrādātāji nejauši zaudē precizitāti.
const myBigInt = 100n;
const myNumber = 50;
try {
const result = myBigInt + myNumber; // This will throw an error
} catch (error) {
console.log(error); // TypeError: Cannot mix BigInt and other types, use explicit conversions
}
Pareizā pieeja: Skaidra konvertēšana
Lai veiktu operāciju starp BigInt un Number, jums ir skaidri jākonvertē viens no tiem.
const myBigInt = 100n;
const myNumber = 50;
// Convert Number to BigInt (safe)
const result1 = myBigInt + BigInt(myNumber);
console.log(result1); // 150n
// Convert BigInt to Number (potentially unsafe!)
const veryLargeBigInt = 900719925474099199n;
// This will lose precision!
const unsafeNumber = Number(veryLargeBigInt);
console.log(unsafeNumber); // 900719925474099200 - The value has been rounded!
const safeResult = Number(100n) + myNumber;
console.log(safeResult); // 150
Kritisks noteikums: Konvertējiet BigInt uz Number tikai tad, ja esat pilnīgi pārliecināts, ka tas ietilpst drošajā veselo skaitļu diapazonā. Pretējā gadījumā vienmēr konvertējiet Number uz BigInt, lai saglabātu precizitāti.
Praktiski BigInt pielietojuma gadījumi globālā kontekstā
Nepieciešamība pēc BigInt nav abstrakta akadēmiska problēma. Tā risina reālas problēmas, ar kurām saskaras izstrādātāji dažādās starptautiskās jomās.
1. Augstas precizitātes laikspiedoli
JavaScript `Date.now()` atgriež milisekunžu skaitu kopš Unix laikmeta sākuma. Lai gan tas ir pietiekami daudzām tīmekļa lietojumprogrammām, tas nav pietiekami detalizēts augstas veiktspējas sistēmām. Daudzas izplatītas sistēmas, datu bāzes un žurnalēšanas ietvari visā pasaulē izmanto nanosekunžu precizitātes laikspiedolus, lai precīzi sakārtotu notikumus. Šie laikspiedoli bieži tiek attēloti kā 64 bitu veseli skaitļi, kas ir pārāk lieli Number
tipam.
// A timestamp from a high-resolution system (e.g., in nanoseconds)
const nanoTimestampStr = "1670000000123456789";
// Using Number results in precision loss
const lostPrecision = Number(nanoTimestampStr);
console.log(lostPrecision); // 1670000000123456800 - Incorrect!
// Using BigInt preserves it perfectly
const correctTimestamp = BigInt(nanoTimestampStr);
console.log(correctTimestamp.toString()); // "1670000000123456789"
// We can now perform accurate calculations
const oneSecondInNanos = 1_000_000_000n;
const nextSecond = correctTimestamp + oneSecondInNanos;
console.log(nextSecond.toString()); // "1670001000123456789"
2. Unikālie identifikatori (ID) no API
Ļoti izplatīts scenārijs ir mijiedarbība ar API, kas izmanto 64 bitu veselus skaitļus unikāliem objektu ID. Šo modeli izmanto lielas globālas platformas, piemēram, Twitter (Snowflake ID) un daudzas datu bāzu sistēmas (piem., `BIGINT` tips SQL).
Kad jūs iegūstat datus no šāda API, JSON parsētājs jūsu pārlūkprogrammā vai Node.js vidē var mēģināt parsēt šo lielo ID kā Number
, kas noved pie datu bojāšanas, pirms jums pat ir iespēja ar to strādāt.
// A typical JSON response from an API
// Note: The ID is a large number, not a string.
const jsonResponse = '{"id": 1367874743838343168, "text": "Hello, world!"}';
// Standard JSON.parse will corrupt the ID
const parsedData = JSON.parse(jsonResponse);
console.log(parsedData.id); // 1367874743838343200 - Wrong ID!
// Solution: Ensure the API sends large IDs as strings.
const safeJsonResponse = '{"id": "1367874743838343168", "text": "Hello, world!"}';
const safeParsedData = JSON.parse(safeJsonResponse);
const userId = BigInt(safeParsedData.id);
console.log(userId); // 1367874743838343168n - Correct!
Tāpēc tā ir plaši pieņemta labākā prakse API visā pasaulē serializēt lielus veselo skaitļu ID kā virknes JSON datos, lai nodrošinātu saderību ar visiem klientiem.
3. Kriptogrāfija
Mūsdienu kriptogrāfija ir balstīta uz matemātiku, kas ietver ārkārtīgi lielus veselus skaitļus. Algoritmi, piemēram, RSA, paļaujas uz operācijām ar skaitļiem, kas ir simtiem vai pat tūkstošiem bitu gari. BigInt ļauj veikt šos aprēķinus dabiski JavaScript, kas ir būtiski tīmekļa bāzes kriptogrāfijas lietojumprogrammām, piemēram, tām, kas izmanto Web Crypto API vai implementē protokolus Node.js.
Lai gan pilnvērtīgs kriptogrāfijas piemērs ir sarežģīts, mēs varam redzēt konceptuālu demonstrāciju:
// Two very large prime numbers (for demonstration purposes only)
const p = 1143400375533529n;
const q = 982451653n; // A smaller one for the example
// In RSA, you multiply them to get the modulus
const n = p * q;
console.log(n.toString()); // "1123281328905333100311297"
// This calculation would be impossible with the Number type.
// BigInt handles it effortlessly.
4. Finanšu un blokķēdes lietojumprogrammas
Strādājot ar finansēm, īpaši kriptovalūtu kontekstā, precizitāte ir vissvarīgākā. Daudzas kriptovalūtas, piemēram, Bitcoin, mēra vērtību savā mazākajā vienībā (piem., satoshi). Šo vienību kopējais piedāvājums var viegli pārsniegt Number.MAX_SAFE_INTEGER
. BigInt ir ideāls rīks, lai apstrādātu šos lielos, precīzos daudzumus, neizmantojot peldošā punkta aritmētiku, kas ir pakļauta noapaļošanas kļūdām.
// 1 Bitcoin = 100,000,000 satoshis
const satoshisPerBTC = 100_000_000n;
// Total supply of Bitcoin is 21 million
const totalBTCSupply = 21_000_000n;
// Calculate total satoshis
const totalSatoshis = totalBTCSupply * satoshisPerBTC;
// 2,100,000,000,000,000 - This is 2.1 quadrillion
console.log(totalSatoshis.toString());
// This value is larger than Number.MAX_SAFE_INTEGER
console.log(totalSatoshis > BigInt(Number.MAX_SAFE_INTEGER)); // true
Padziļinātas tēmas un biežākās kļūdas
Serializācija un JSON.stringify()
Viena no visbiežāk sastopamajām problēmām, ar ko saskaras izstrādātāji, ir objektu, kas satur BigInt, serializācija. Pēc noklusējuma JSON.stringify()
nezina, kā apstrādāt bigint
tipu, un izmetīs 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
}
1. risinājums: Implementējiet `toJSON` metodi
Jūs varat norādīt JSON.stringify
, kā apstrādāt BigInt, pievienojot `toJSON` metodi BigInt.prototype
. Šī pieeja labo globālo prototipu, kas varētu būt nevēlami dažās koplietojamās vidēs, bet tā ir ļoti efektīva.
// A global patch. Use with consideration.
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"}'
2. risinājums: Izmantojiet aizstājējfunkciju (replacer)
Drošāka, vairāk lokalizēta pieeja ir izmantot `replacer` argumentu JSON.stringify
. Šī funkcija tiek izsaukta katram atslēgas/vērtības pārim un ļauj jums pārveidot vērtību pirms serializācijas.
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"}'
Bitu operācijas
BigInt atbalsta visus bitu operatorus, ar kuriem esat pazīstami no Number
tipa: &
(AND), |
(OR), ^
(XOR), ~
(NOT), <<
(kreisā nobīde) un >>
(zīmi saglabājoša labā nobīde). Tie ir īpaši noderīgi, strādājot ar zema līmeņa datu formātiem, atļaujām vai noteikta veida algoritmiem.
const permissions = 5n; // 0101 in binary
const READ_PERMISSION = 4n; // 0100
const WRITE_PERMISSION = 2n; // 0010
// Check if read permission is set
console.log((permissions & READ_PERMISSION) > 0n); // true
// Check if write permission is set
console.log((permissions & WRITE_PERMISSION) > 0n); // false
// Add write permission
const newPermissions = permissions | WRITE_PERMISSION;
console.log(newPermissions); // 7n (which is 0111)
Veiktspējas apsvērumi
Lai gan BigInt ir neticami spēcīgs, ir svarīgi saprast tā veiktspējas īpašības:
- Number pret BigInt: Veseliem skaitļiem drošajā diapazonā standarta
Number
operācijas ir ievērojami ātrākas. Tas ir tāpēc, ka tās bieži var tieši kartēt uz mašīnlīmeņa instrukcijām, ko apstrādā datora CPU. BigInt operācijas, kas ir patvaļīga izmēra, prasa sarežģītākus programmatūras algoritmus. - BigInt pret bibliotēkām: Dabiskais `BigInt` parasti ir daudz ātrāks nekā uz JavaScript balstītas lielo skaitļu bibliotēkas. Implementācija ir daļa no JavaScript dzinēja (piemēram, V8 vai SpiderMonkey) un ir rakstīta zemāka līmeņa valodā, piemēram, C++, kas tai dod ievērojamu veiktspējas priekšrocību.
Zelta likums: Izmantojiet Number
visiem skaitliskajiem aprēķiniem, ja vien jums nav konkrēta iemesla uzskatīt, ka vērtības varētu pārsniegt Number.MAX_SAFE_INTEGER
. Izmantojiet BigInt
, kad jums ir nepieciešamas tā iespējas, nevis kā noklusējuma aizstājēju visiem skaitļiem.
Pārlūkprogrammu un vides saderība
BigInt ir moderna JavaScript funkcija, bet tās atbalsts tagad ir plaši izplatīts visā globālajā ekosistēmā.
- Tīmekļa pārlūkprogrammas: Atbalstīts visās lielākajās modernajās pārlūkprogrammās (Chrome 67+, Firefox 68+, Safari 14+, Edge 79+).
- Node.js: Atbalstīts kopš versijas 10.4.0.
Projektiem, kuriem jāatbalsta ļoti vecas vides, transpilācija, izmantojot tādus rīkus kā Babel, var būt risinājums, bet tas nāk ar veiktspējas sodu. Ņemot vērā plašo atbalstu mūsdienās, lielākā daļa jauno projektu var izmantot BigInt dabiski bez bažām.
Noslēgums un labākās prakses
BigInt ir spēcīgs un būtisks papildinājums JavaScript valodai. Tas nodrošina dabisku, efektīvu un ergonomisku risinājumu ilgstošajai lielo veselo skaitļu aritmētikas problēmai, ļaujot veidot jaunas klases lietojumprogrammas ar JavaScript, sākot no kriptogrāfijas līdz augstas precizitātes datu apstrādei.
Lai to efektīvi izmantotu un izvairītos no biežākajām kļūdām, paturiet prātā šīs labākās prakses:
- Izmantojiet `n` sufiksu: Dodiet priekšroku `123n` literāļa sintaksei, lai izveidotu BigInt. Tā ir skaidra, kodolīga un novērš iespējamu precizitātes zudumu izveides laikā.
- Nejauciet tipus: Atcerieties, ka aritmētiskajās operācijās nevarat sajaukt BigInt un Number. Esiet skaidri ar savām konversijām: `BigInt()` vai `Number()`.
- Prioritizējiet precizitāti: Konvertējot starp tipiem, vienmēr dodiet priekšroku `Number` konvertēšanai uz `BigInt`, lai novērstu nejaušu precizitātes zudumu.
- Izmantojiet stingro vienādību: Izmantojiet `===` nevis `==` salīdzināšanai, lai izvairītos no mulsinošas uzvedības, ko izraisa tipu piespiešana.
- Risiniet JSON serializāciju: Plānojiet BigInt serializāciju. Izmantojiet pielāgotu `replacer` funkciju `JSON.stringify`, lai iegūtu drošu, ne-globālu risinājumu.
- Izvēlieties pareizo rīku: Izmantojiet `Number` vispārējas nozīmes matemātikai drošā veselo skaitļu diapazonā labākai veiktspējai. Izmantojiet `BigInt` tikai tad, kad jums patiešām ir nepieciešamas tā patvaļīgās precizitātes iespējas.
Pieņemot BigInt un izprotot tā noteikumus, jūs varat rakstīt stabilākas, precīzākas un jaudīgākas JavaScript lietojumprogrammas, kas spēj tikt galā ar jebkura mēroga skaitliskiem izaicinājumiem.