JavaScript Ready | Программирование
Почему JavaScript может изменить большой ID из API
Числовой идентификатор может быть корректно сформирован на сервере, но изменить значение после преобразования в js Number.
Причина заключается в модели представления чисел: Number соответствует IEEE 754 binary64 и не способен точно представить все целые числа произвольного размера:
const id = 9007199254740993;
console.log(id);
// 9007199254740992
Граница гарантированно точного представления целых чисел определяется константой Number.MAX_SAFE_INTEGER:
console.log(Number.MAX_SAFE_INTEGER);
// 9007199254740991
console.log(
Number.isSafeInteger(9007199254740991)
);
// true
Безопасный диапазон составляет от -(2^53 - 1) до 2^53 - 1. За его пределами проблема заключается не просто в том, что число становится «слишком большим»: соседние математические целые уже не обязаны иметь разные представления в Number. Поэтому два разных исходных значения могут оказаться одним и тем же значением javascript:
const a = 9007199254740992;
const b = 9007199254740993;
console.log(a === b);
// true
Для точных операций с большими целыми в js существует BigInt. Суффикс n создаёт значение непосредственно как BigInt, без промежуточного преобразования в Number:
const id = 9007199254740993n;
console.log(id + 1n);
// 9007199254740994n
С API возникает дополнительная граница — десериализация JSON. Сам JSON может содержать последовательность цифр 9007199254740993 как число, но стандартный JSON.parse() преобразует JSON-число в JavaScript Number.
Если значение находится за пределами безопасного целочисленного диапазона, точность теряется уже во время этого преобразования:
const data = JSON.parse(
'{"id":9007199254740993}'
);
console.log(data.id);
// 9007199254740992
После такого парсинга восстановить исходный ID по полученному Number в общем случае невозможно: информация о первоначальном целом уже потеряна.
Поэтому идентификаторы, над которыми не предполагается арифметика, надёжнее передавать в JSON как строки. Это сохраняет исходную последовательность цифр независимо от числовых ограничений JavaScript-клиента:
const data = JSON.parse(
'{"id":"9007199254740993"}'
);
console.log(data.id);
// "9007199254740993"
Если значение всё же требуется использовать в точных целочисленных вычислениях, строку можно явно преобразовать в BigInt уже после получения ответа API:
const data = JSON.parse(
'{"id":"9007199254740993"}'
);
const id = BigInt(data.id);
console.log(id + 1n);
// 9007199254740994n
🔥 Числовая природа ID не означает, что его следует представлять типом Number. Если идентификатор потенциально превышает Number.MAX_SAFE_INTEGER, его представление становится частью корректности API-контракта: строка сохраняет ID без потерь при передаче, а BigInt подходит там, где действительно требуется точная целочисленная арифметика.
📣 JS Ready | #практика
12 · 1.1K ·