Български

Изчерпателно ръководство за типа BigInt в JavaScript, обхващащо неговите характеристики, употреба и приложения при работа с големи цели числа. Научете как да преодолеете ограниченията на JavaScript и да извършвате сложни изчисления с точност.

JavaScript BigInt: Овладяване на аритметика с големи цели числа

JavaScript, макар и универсален език, има ограничения при работа с много големи цели числа. Стандартният тип `Number` може точно да представя цели числа само до определена граница, известна като `Number.MAX_SAFE_INTEGER`. Отвъд тази граница изчисленията стават неточни, което води до неочаквани резултати. Тук на помощ идва BigInt. Въведен в ECMAScript 2020, BigInt е вграден обект, който предоставя начин за представяне и манипулиране на цели числа с произволен размер, надхвърляйки ограниченията на стандартния тип `Number`.

Разбиране на нуждата от BigInt

Преди BigInt, разработчиците на JavaScript трябваше да разчитат на библиотеки или персонализирани имплементации за обработка на изчисления с големи цели числа. Тези решения често водеха до намалена производителност и повишена сложност. Въвеждането на BigInt предостави вграден и ефективен начин за работа с големи цели числа, отваряйки възможности за приложения в различни области, включително:

Създаване на BigInt стойности

Има два основни начина за създаване на BigInt стойности в JavaScript:

  1. Използване на конструктора `BigInt()`: Този конструктор може да преобразува число, низ или булева стойност в BigInt.
  2. Използване на суфикса `n`: Добавянето на `n` към цяло число създава BigInt.

Примери:

Използване на конструктора `BigInt()`:


const bigIntFromNumber = BigInt(12345678901234567890);
const bigIntFromString = BigInt("98765432109876543210");
const bigIntFromBoolean = BigInt(true); // Резултатът е 1n
const bigIntFromFalseBoolean = BigInt(false); // Резултатът е 0n

console.log(bigIntFromNumber); // Изход: 12345678901234567890n
console.log(bigIntFromString); // Изход: 98765432109876543210n
console.log(bigIntFromBoolean); // Изход: 1n
console.log(bigIntFromFalseBoolean); // Изход: 0n

Използване на суфикса `n`:


const bigIntLiteral = 12345678901234567890n;
console.log(bigIntLiteral); // Изход: 12345678901234567890n

Важна забележка: Не можете директно да смесвате BigInt и Number стойности в аритметични операции. Трябва изрично да ги преобразувате в един и същ тип, преди да извършвате изчисления. Опитът за директното им смесване ще доведе до `TypeError`.

Аритметични операции с BigInt

BigInt поддържа повечето от стандартните аритметични оператори, включително:

Примери:


const a = 12345678901234567890n;
const b = 98765432109876543210n;

const sum = a + b;
const difference = a - b;
const product = a * b;
const quotient = a / 2n; // Забележка: Делението закръглява към нула
const remainder = a % 7n;
const power = a ** 3n; // Степенуването работи както се очаква

console.log("Сума:", sum); // Изход: Сума: 111111111011111111100n
console.log("Разлика:", difference); // Изход: Разлика: -86419753208641975320n
console.log("Произведение:", product); // Изход: Произведение: 1219326311370217957951669538098765432100n
console.log("Частно:", quotient); // Изход: Частно: 6172839450617283945n
console.log("Остатък:", remainder); // Изход: Остатък: 5n
console.log("Степен:", power); // Изход: Степен: 187641281029182300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n

Важни съображения:

Оператори за сравнение

Можете да използвате стандартни оператори за сравнение (`==`, `!=`, `<`, `>`, `<=`, `>=`), за да сравнявате BigInt стойности с други BigInt стойности или дори с Number стойности. Въпреки това, имайте предвид потенциала за преобразуване на типове.

Примери:


const a = 10n;
const b = 20n;
const c = 10;

console.log(a == b);   // Изход: false
console.log(a != b);   // Изход: true
console.log(a < b);    // Изход: true
console.log(a > b);    // Изход: false
console.log(a <= b);   // Изход: true
console.log(a >= b);   // Изход: false

console.log(a == c);   // Изход: true (преобразуване на типове)
console.log(a === c);  // Изход: false (няма преобразуване на типове)

Добра практика: Използвайте стриктно равенство (`===`) и стриктно неравенство (`!==`), за да избегнете неочаквано преобразуване на типове при сравняване на BigInt и Number стойности.

Преобразуване между BigInt и Number

Въпреки че директните аритметични операции между BigInt и Number не са позволени, можете да преобразувате между двата типа. Все пак, имайте предвид потенциалната загуба на прецизност при преобразуване на BigInt в Number, ако стойността на BigInt надвишава `Number.MAX_SAFE_INTEGER`.

Примери:


const bigIntValue = 9007199254740991n; // Number.MAX_SAFE_INTEGER
const numberValue = Number(bigIntValue); // Преобразуване на BigInt в Number
console.log(numberValue); // Изход: 9007199254740991

const largerBigIntValue = 9007199254740992n; // Надхвърля Number.MAX_SAFE_INTEGER
const largerNumberValue = Number(largerBigIntValue);
console.log(largerNumberValue); // Изход: 9007199254740992 (може да е неточно)

const numberToBigInt = BigInt(12345); // Преобразуване на Number в BigInt
console.log(numberToBigInt); // Изход: 12345n

Случаи на употреба и примери

Криптография

Криптографските алгоритми често разчитат на много големи прости числа за сигурност. BigInt предоставя начин за ефективно представяне и манипулиране на тези числа.


// Пример: Генериране на проста (несигурна) двойка ключове
function generateKeyPair() {
  const p = 281n; // Просто число
  const q = 283n; // Друго просто число
  const n = p * q; // Модул
  const totient = (p - 1n) * (q - 1n); // Функция на Ойлер

  // Изберете e (публичен експонент), така че 1 < e < totient и gcd(e, totient) = 1
  const e = 17n;

  // Изчислете d (частен експонент), така че (d * e) % totient = 1
  let d = 0n;
  for (let i = 1n; i < totient; i++) {
    if ((i * e) % totient === 1n) {
      d = i;
      break;
    }
  }

  return {
    publicKey: { n, e },
    privateKey: { n, d },
  };
}

const keyPair = generateKeyPair();
console.log("Публичен ключ:", keyPair.publicKey);
console.log("Частен ключ:", keyPair.privateKey);

Забележка: Това е опростен пример само за демонстрационни цели. Криптографията в реалния свят използва много по-големи прости числа и по-сложни алгоритми.

Финансови изчисления

При работа с големи суми пари, особено при международни транзакции, прецизността е от решаващо значение. BigInt може да предотврати грешки при закръгляване и да осигури точни изчисления.


// Пример: Изчисляване на сложна лихва
function calculateCompoundInterest(principal, rate, time) {
  const principalBigInt = BigInt(principal * 100); // Преобразуване в стотинки
  const rateBigInt = BigInt(rate * 10000);       // Преобразуване в десетохилядни от процента
  const timeBigInt = BigInt(time);

  let amount = principalBigInt;
  for (let i = 0n; i < timeBigInt; i++) {
    amount = amount * (10000n + rateBigInt) / 10000n;
  }

  const amountInDollars = Number(amount) / 100;
  return amountInDollars;
}

const principal = 1000000; // $1,000,000
const rate = 0.05;    // 5% лихвен процент
const time = 10;     // 10 години

const finalAmount = calculateCompoundInterest(principal, rate, time);
console.log("Крайна сума:", finalAmount); // Изход: Крайна сума: 1628894.6267774413 (приблизително)

В този пример преобразуваме главницата и лихвения процент в BigInt стойности, за да избегнем грешки при закръгляване по време на изчислението. След това резултатът се преобразува обратно в Number за показване.

Работа с големи идентификатори (ID)

В разпределените системи генерирането на уникални ID-та на множество сървъри може да бъде предизвикателство. Използването на BigInt ви позволява да създавате много големи ID-та, които е малко вероятно да се сблъскат.


// Пример: Генериране на уникален ID на базата на времеви маркер и ID на сървъра
function generateUniqueId(serverId) {
  const timestamp = BigInt(Date.now());
  const serverIdBigInt = BigInt(serverId);
  const random = BigInt(Math.floor(Math.random() * 1000)); // Добавяне на малко случайност

  // Комбинирайте стойностите, за да създадете уникален ID
  const uniqueId = (timestamp << 20n) + (serverIdBigInt << 10n) + random;
  return uniqueId.toString(); // Върнете като низ за лесна обработка
}

const serverId = 123; // Примерно ID на сървър
const id1 = generateUniqueId(serverId);
const id2 = generateUniqueId(serverId);

console.log("Уникален ID 1:", id1);
console.log("Уникален ID 2:", id2);

BigInt и JSON

Стойностите BigInt не се поддържат нативно от JSON. Опитът да се сериализира JavaScript обект, съдържащ BigInt, с помощта на `JSON.stringify()` ще доведе до `TypeError`. За да обработвате BigInt стойности при работа с JSON, имате няколко възможности:

  1. Преобразуване в низ: Преобразувайте BigInt в низ преди сериализация. Това е най-често срещаният и лесен подход.
  2. Персонализирана сериализация/десериализация: Използвайте персонализирана функция за сериализация/десериализация, за да обработвате BigInt стойности.

Примери:

Преобразуване в низ:


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Преобразувайте BigInt в низ преди сериализация
data.id = data.id.toString();

const jsonData = JSON.stringify(data);
console.log(jsonData); // Изход: {"id":"12345678901234567890","name":"Example Data"}

// При десериализация ще трябва да преобразувате низа обратно в BigInt
const parsedData = JSON.parse(jsonData, (key, value) => {
  if (key === "id") {
    return BigInt(value);
  }
  return value;
});

console.log(parsedData.id); // Изход: 12345678901234567890n

Персонализирана сериализация/десериализация (с `replacer` и `reviver`):


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Персонализирана сериализация
const jsonData = JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') {
    return value.toString();
  } else {
    return value;
  }
});

console.log(jsonData);

// Персонализирана десериализация
const parsedData = JSON.parse(jsonData, (key, value) => {
    if (typeof value === 'string' && /^[0-9]+$/.test(value)) { //проверете дали е число и низ
      try {
        return BigInt(value);
      } catch(e) {
          return value;
      }
    }
    return value;
});

console.log(parsedData.id);

Съвместимост с браузъри

BigInt се поддържа широко в съвременните браузъри. Въпреки това е важно да се провери съвместимостта за по-стари браузъри или среди. Можете да използвате инструмент като Can I use, за да проверите поддръжката от браузърите. Ако трябва да поддържате по-стари браузъри, може да обмислите използването на полифил, но имайте предвид, че полифилите могат да повлияят на производителността.

Съображения относно производителността

Въпреки че BigInt предоставя мощен начин за работа с големи цели числа, е важно да сте наясно с потенциалните последици за производителността.

Следователно, използвайте BigInt само когато е необходимо и оптимизирайте кода си за производителност, ако извършвате голям брой операции с BigInt.

Заключение

BigInt е ценно допълнение към JavaScript, което позволява на разработчиците да обработват аритметика с големи цели числа с прецизност. Като разбирате неговите характеристики, ограничения и случаи на употреба, можете да използвате BigInt за изграждане на стабилни и точни приложения в различни области, включително криптография, финансови изчисления и научни изчисления. Не забравяйте да вземете предвид съвместимостта с браузърите и последиците за производителността, когато използвате BigInt във вашите проекти.

За допълнително проучване

Това ръководство предоставя изчерпателен преглед на BigInt в JavaScript. Разгледайте свързаните ресурси за по-задълбочена информация и напреднали техники.