一个JSON.parse的问题,让我丢掉了字节的 offer!

news2024/12/23 22:54:46

前端训练营:1v1私教,终身辅导计划,帮你拿到满意的 offer 已帮助数百位同学拿到了中大厂 offer。微信在文章底部,欢迎来撩~~~~~~~~

Hello,大家好,我是 Sunday。

在几年前的一次字节跳动面试中,面试官提出的一个关于 BigInt 的问题让我印象深刻。那时,我对 BigInt 还知之甚少,但这个问题引起了我对它的好奇心。今天,我想和大家深入探讨一下 BigInt 的奥秘,以及它在解决 JSON.parse 大型数字解析问题中的应用。

本文部分内容引自《Why does JSON.parse corrupt large numbers and how to solve this?》这篇文章。

01:JSON.parse 丢失数字精度

绝大多数网络应用都会涉及从服务器获取数据,这些数据以 JSON 格式接收,并被解析为 JavaScript 中的对象或数组,以供前端页面使用。通常,我们会使用 JavaScript 内置的 JSON.parse 函数来进行数据解析,这个过程既高效又便捷。

JSON 数据格式非常简单,它实际上是 JavaScript 的子集,因此可以与 JavaScript 完全互换。许多时候,前端开发者不会怀疑 JavaScript 中的 JSON 数据会出现问题,但有些情况却不行。

比如以下场景:

{"count": 9123372036854000123}

当将其解析为 JavaScript 并读取 count 时,会得到如下的值:

9123372036854000000

很显然,此时解析的值是不准确的,最后三位数字变成了零。

02:为什么 JSON.parse 会损坏数字?

当类似于 7584775647658465744 的长数字出现时,它不仅是有效的 JSON,也是有效的 JavaScript。然而,在 JavaScript 中将这样的值解析为数字时,问题就会显现出来。

这是因为 JavaScript 最初只有一种数字类型 Number,它是一种 64 位浮点值,类似于 C++、Java 或 C# 中的 Double 值,可以存储大约 16 位数字。 因此,像 9123372036854000123 这样的 19 位数字无法被完全表示。 在这种情况下,最后三位数字会丢失,导致数值损坏。

类似的情况也会出现在处理分数时。比如,当开发者在 JavaScript 中计算 1/3 时,结果如下:

console.log(1 / 3); // 输出结果为 0.3333333333333333

实际上,该值应该是一个具有无限位数小数的结果,但 JavaScript 数字在大约 16 位数字后就会结束。

那么,JSON 文档中像 9123372036854000123 这样的大数值是如何产生的呢? 其实,这源于其他编程语言,比如 Java 或 C#,这些语言具有不同的数字数据类型(比如 Long)。 Long 类型是一个 64 位值,可以容纳最多约 20 位的整数值,而它不需要像浮点值那样存储指数值(Exponential Value)。

因此,在类似 Java 的语言中,开发者可能会拥有一个 JavaScript 的 Number 类型无法正确表示的 Long 值,或者在其他语言中类似的 Double 类型也无法准确表示。

JavaScript 的 Number 类型还有一些其他限制:该值可能会溢出或下溢。 例如,1e+500 将变为无穷大,而 1e-500 将变为 0。但是,在实际应用中,这些限制很少成为问题。

03:如何防止数字被 JSON.parse 损坏

第一个解决方案是在 JSON.parse 中利用一个可选的 reviver 参数,它允许开发者以不同的方式解析内容。

如果指定了 reviver 函数,则解析出的 JavaScript 值(解析值)会经过一次转换后才将被最终返回(返回值)。

更具体点讲就是:解析值本身以及它所包含的所有属性,会按照一定的顺序(从最最里层的属性开始,一级级往外,最终到达顶层,也就是解析值本身)分别的去调用 reviver 函数,在调用过程中,当前属性所属的对象会作为 this 值,当前属性名和属性值会分别作为第一个和第二个参数传入 reviver 中。

如果 reviver 返回 undefined,则当前属性会从所属对象中删除,如果返回了其他值,则返回的值会成为当前属性新的属性值。

当遍历到最顶层的值(解析值)时,传入 reviver 函数的参数会是空字符串 “”(因为此时已经没有真正的属性)和当前的解析值(有可能已经被修改过了),当前的 this 值会是 {“”: 修改过的解析值},在编写 reviver 函数时,要注意到这个特例。(这个函数的遍历顺序依照:从最内层开始,按照层级顺序,依次向外遍历)

JSON.parse(
  '{"1": 1,"2": 2,"3": {"4": 4,"5": {"6": 6}}}',
  function (k, v) {
    console.log(k);
    // 输出当前的属性名,从而得知遍历顺序是从内向外的,
    // 最后一个属性名会是个空字符串。
    return v;
    // 返回原始属性值,相当于没有传递 reviver 参数。
  }
);

但是,当 JSON.parse 开始解析 JSON 字符串时,它首先会将数字识别为 JavaScript 中的标准数字类型。即使在 reviver 参数执行之前,数字已经被处理并存储为 JavaScript 中的数字表示形式,超出浮点数范围的大数字在此阶段就已损坏。

因此,尽管 reviver 允许对解析后的值进行修改和处理,但它无法解决数字损坏的问题,因为该问题在 JSON.parse 内部处理数字时就已经发生。

所以,开发者无法仅凭内置的 JSON.parse 来解决问题,必须寻找其他的 JSON 解析器。 幸运的是,存在许多出色的解决方案可供选择。

以下是一些优秀的开源库,专门应对 JSON.parse 的各种问题,可根据实际需求自行选择使用。

3-1:json-bigint

JSON.parse 和 JSON.tringify 的实现,支持 bigints 。 基于 Douglas Crockford JSON.js 包和 bignumber.js 库。

var JSONbig = require('json-bigint');

var json = '{"value": 9223372036854775807,"v2": 123}';
console.log('Input:', json);
console.log('');

console.log('node.js built-in JSON:');
var r = JSON.parse(json);
console.log('JSON.parse(input).value :', r.value.toString());
console.log('JSON.stringify(JSON.parse(input)):', JSON.stringify(r));

console.log('\n\nbig number JSON:');
var r1 = JSONbig.parse(json);
console.log('JSONbig.parse(input).value :', r1.value.toString());
console.log('JSONbig.stringify(JSONbig.parse(input)):', JSONbig.stringify(r1));

输出结果如下:

Input: {"value" : 9223372036854775807, "v2": 123}

node.js built-in JSON:
JSON.parse(input).value :  9223372036854776000
JSON.stringify(JSON.parse(input)): {"value":9223372036854776000,"v2":123}


big number JSON:
JSONbig.parse(input).value :  9223372036854775807
JSONbig.stringify(JSONbig.parse(input)): {"value":9223372036854775807,"v2":123}

3-2:lossless-json

lossless-json 用于解析 JSON,而且不会有丢失数字信息的风险。基础用法如下:

import {parse, stringify} from 'lossless-json'

const text = '{"decimal":2.370,"long":9123372036854000123,"big":2.3e+500}'

// JSON.parse will lose some digits and a whole number:
console.log(JSON.stringify(JSON.parse(text)))
// '{"decimal":2.37,"long":9123372036854000000,"big":null}'
// WHOOPS!!!

// LosslessJSON.parse will preserve all numbers and even the formatting:
console.log(stringify(parse(text)))
// '{"decimal":2.370,"long":9123372036854000123,"big":2.3e+500}'

这个库的运作方式与本机的 JSON.parse 和 JSON.stringify 完全相同。然而,它与标准方法的不同之处在于 lossless-json 保留了大数字的完整信息。

在这里,它并不将数值解析为标准的数字类型,而是将其解析为 LosslessNumber,这是一个将数值以字符串形式存储的轻量级类。开发者可以使用 LosslessNumber 执行一般的操作,但如果这些操作会导致信息丢失,该类将抛出错误。

3-3:js-json-bigint

js-json-bigint 是一个 JavaScript 库,它允许使用 BigInt 来支持对 JSON 进行编码。如果需要在服务器中处理 64 位整数,该库能够满足这一需求,因为它将 64 位整数解析为 bigint。与此同时,这个库没有任何依赖,并且只有 443 字节的体积。

该库的实现非常简洁,核心代码仅有大约 20 多行:

export function parseJSON(text, reviver) {
	if (typeof text !== 'string') {
		return null
	}
	return JSON.parse(text.replace(/([^\"]+\"\:\s*)(\d{16,})(\,\s*\"[^\"]+|}$)/g, '$1"$2n"$3'), (k, v) => {
		if (typeof v === 'string' && /^\d{16,}n$/.test(v)) {
			v = BigInt(v.slice(0, -1))
		}
		return typeof reviver === 'function' ? reviver(k, v) : v
	})
}

export function stringifyJSON(value, replacer, space) {
	return JSON.stringify(value, (k, v) => {
		if (typeof v === 'bigint') {
			v = v.toString() + 'n'
		}
		return typeof replacer === 'function' ? replacer(k, v) : v
	}, space).replace(/([^\"]+\"\:\s*)(?:\")(\d{16,})(?:n\")(\,\s*\"[^\"]+|}$)/g, '$1$2$3')
}

3-4:BigInt 方案

不涉及第三方库,使用 BigInt 值也可能会导致棘手的问题。 当混合使用大整数和常规数字时,JavaScript 可以默默地将一种数字类型强制转换为另一种数字类型,从而导致错误。

const a = 91111111111111e3 // a regular number
const b = 91111111111111000n // a bigint
console.log(a == b)
// 返回 false (应该是 true)
console.log(a> b)
// 返回 true (应该是 false)

比如,以上示例会看到两个常量 a 和 b 持有相同的数值。 但一个是数字,另一个是 BigInt,使用 == 和 > 等常规运算符可能会导致错误的结果。

总之,最好的办法是从一开始就尽量避免与大数字打交道,同时为了防止陷入与 BigInt 数据类型相关的难以调试的问题,使用 TypeScript 显式定义数据模型会很有帮助。

不过,值得一提的是关于 “JSON.parse source text access”的 TC39 proposal 提案已经被提出

该提案扩展了 JSON.parse 行为以授予 reviver 函数对输入源文本的访问权限并扩展 JSON.stringify 行为以支持原始 JSON 文本基元的对象占位符的提案。

const digitsToBigInt = (key, val, {source}) =>
  /^[0-9]+$/.test(source) ? BigInt(source) : val;

const bigIntToRawJSON = (key, val) =>
  typeof val === "bigint" ? JSON.rawJSON(String(val)) : val;

const tooBigForNumber = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
JSON.parse(String(tooBigForNumber), digitsToBigInt) === tooBigForNumber;
// → true

const wayTooBig = BigInt("1" + "0".repeat(1000));
JSON.parse(String(wayTooBig), digitsToBigInt) === wayTooBig;
// → true

const embedded = JSON.stringify({ tooBigForNumber }, bigIntToRawJSON);
embedded === '{"tooBigForNumber":9007199254740993}';
// → true

1v1私教,帮大家拿到满意的 offer

我目前在做一个 前端训练营 ,主打的就是:1v1 私教,帮大家拿到满意的 offer 。

可以点击这里查看详情

也可以直接加我微信沟通,备注【训练营】:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1352527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HelloWorld搭建(第一种模型)

1.创建Springboot项目并且引入依赖 <!-- 引入RabbitMQ的相关依赖 --> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.7.2</version> </dependency> 2.第一种模式(直连…

企业生产安全指南,请点击文章查收!

随着科技的不断发展&#xff0c;视频监控系统在各个领域中扮演着越来越重要的角色。视频监控系统为企业和机构提供了强大的工具&#xff0c;以提高效率、增强安全性&#xff0c;并为决策制定提供实时数据支持。 客户案例 企业安全与生产管理 在现代企业环境中&#xff0c;保障…

产品|燕窝中的“秘密武器”——燕窝酸

前言 当提及燕窝&#xff0c;大部分人脑海中首先会闪过的大概是“宫廷圣品”、“名贵补品”等听上去十分高大上的形容词。然而随着现代人们生活水平的提高&#xff0c;燕窝已不再神秘&#xff0c;逐渐成为寻常百姓餐桌上的常见食品之一。据我国中医记载&#xff0c;燕窝具有养…

职场商务英语口语“自助餐”用英文怎么说?柯桥外语培训

“自助餐”用英语怎么说&#xff1f; ● 其实很简单&#xff0c;“自助餐”的英文就是&#xff1a;Buffet。 例句&#xff1a; At lunchtime, theres a choice between the buffet or the set menu.15857575376 午饭时&#xff0c;可以选择自助餐或套餐。 We are going to …

租赁小程序|北京租赁系统开发|租赁软件推动了行业发展

如今&#xff0c;租赁行业正在迅速发展&#xff0c;越来越多的商家和用户寻求更便捷、高效的租赁体验。租赁小程序作为一种科技创新的产物&#xff0c;为租赁行业带来了巨大的变革。本文将介绍租赁小程序的功能与特色&#xff0c;旨在让商家和用户更了解这一工具&#xff0c;为…

关于标准那些事——第六篇 四象之“朱雀”(要素的表述)

两仪生四象——东方青龙&#xff08;木&#xff09;、西方白虎&#xff08;金&#xff09;、南方朱雀&#xff08;火&#xff09;、北方玄武&#xff08;水&#xff09; 分别对应标准编写之四象——层次的编写、要素的编写、要素的表述、格式的编排。 今天来分享一下 要素的表…

Unity ShaderGraph 技能冷却转圈效果

Unity ShaderGraph 技能冷却转圈效果 前言项目场景布置代码编写ShaderGraph 连线总结 参考 前言 遇到一个需求&#xff0c;要展示技能冷却的圆形遮罩效果。 项目 场景布置 代码编写 Shader核心的就两句 // 将uv坐标系的原点移到纹理中心 float2 uv i.uv - float2(0.5, 0…

基于Springboot+vue医院管理系统(前后端分离)

最近有一些读者问我有没有完整的基于SpringbootVue的项目源码&#xff0c;今天给大家整理了一下&#xff0c;无偿分享给大家。 功能&#xff1a; 医生信息管理 换着信息管理 挂号信息管理 药物信息管理 检查项目管理 病床信息管理 排班信息管理 数据统计分析 开发工具…

仓储物流RFID智能管理设计解决方案

一、项目概况 1、项目背景 在物流管理中&#xff0c;仓储管理是至关重要的环节&#xff0c;传统的仓储管理存在诸多问题&#xff0c;如进出库操作混乱、库存报告延迟、货品属性不清晰、堆放混乱、盘点不准确等&#xff0c;这些问题需要通过基于信息化管理的技术进行彻底改造。…

「Qt Widget中文示例指南」如何实现一个日历?(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文中的CalendarWi…

开源MIT协议软件 - ChatGPT Admin Web

ChatGPT Admin Web 在团队和组织内共享使用人工智能的一站式解决方案。 ​编辑 ​编辑 ​编辑 CAW 是一个自托管网络应用程序&#xff0c;提供开箱即用的用户管理&#xff0c;包括后台界面以及可配置的支付计划和相关支付界面。 GitHub Sponsor / 爱发电 功能 Features …

React使用动态标签名称

最近在一项目里&#xff08;React antd&#xff09;遇到一个需求&#xff0c;某项基础信息里有个图标配置&#xff08;图标用的是antd的Icon组件&#xff09;&#xff0c;该项基础信息的图标信息修改后&#xff0c;存于后台数据库&#xff0c;后台数据库里存的是antd Icon组件…

【办公技巧】word文档怎么隐藏文字呢?word隐藏文字的四种方法!

Word文件制作完毕后&#xff0c;有些部分不想别让人看到&#xff0c;又不想删掉&#xff0c;把么我们可以隐藏文字&#xff0c;今天分享几个隐藏文字的方法给大家~ 方法一&#xff1a; 最简单的方法&#xff0c;将字体颜色与背景颜色设置为一致的&#xff0c;这样就达到了隐藏…

《Vue3 前端构建工具》 Vue-cli 与 Vite 创建项目的插件和配置对比

前言 2024 年 啦&#xff01;Vue2 也于 2023.12.31 寿终正寝 &#xff01; 然而我的 Vue3 升级一再拖延&#xff08;惭愧不已&#xff09;~ 赶起来吧~ 今天用 vue-cli 和 vite 分别创建了 Vue3 项目&#xff0c;具体实现步骤见如下两篇。 《基于 Vue Cli4.x Vue3 TS styl…

论文润色降重多少钱 智能写作

大家好&#xff0c;今天来聊聊论文润色降重多少钱 智能写作&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;论文润色降重多少钱――了解市场行情与选择合…

【MPC学习笔记】02:MPC详细简介(Lecture 1_1 Unconstrained MPC)

本笔记来自北航诸兵老师的课程 课程地址&#xff1a;模型预测控制&#xff08;2022春&#xff09;lecture 1-1 Unconstrained MPC 接上一篇&#xff1a;【MPC学习笔记】01&#xff1a;MPC简介&#xff08;Lecture 1_1 Unconstrained MPC&#xff09; 文章目录 1 详细介绍1.1 状…

​ubuntu安装远程桌面 ​Xfce

本文最先发布在&#xff1a;https://www.itcoder.tech/posts/how-to-install-xrdp-on-ubuntu-20-04/ Xrdp 是一个微软远程桌面协议&#xff08;RDP&#xff09;的开源实现&#xff0c;它允许你通过图形界面控制远程系统。通过 RDP&#xff0c;你可以登录远程机器&#xff0c;并…

从 YOLOv1 到 YOLO-NAS 的所有 YOLO 模型:论文解析

在计算机视觉的浩瀚领域&#xff0c;有一支耀眼的明星&#xff0c;她的名字传颂着革新与突破的传奇——YOLO&#xff08;You Only Look Once&#xff09;。回溯时光&#xff0c;走进这个引人注目的名字背后&#xff0c;我们仿佛穿越进一幅画卷&#xff0c;一幅展现创新魅力与技…

论文润色会泄露吗 智能写作

大家好&#xff0c;今天来聊聊论文润色会泄露吗 智能写作&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;论文润色会泄露吗――保护学术隐私与安全的探讨…

单机游戏推荐:少年骇客:能量之旅 中文免安装版

邪恶的骇尸诅咒了欧洲 - 只有少年骇客能制止他&#xff01;在你奋力拯救世界的冒险历程中&#xff0c;探索这个充满了战斗、谜题和秘密的 3D 世界&#xff01; 游戏介绍 田小班和他的家人们正在享受悠闲的欧洲假期。值此之际&#xff0c;邪恶的骇尸唤醒了四个神秘水晶&#xff…