谨慎使用JSON.stringify

谨慎使用JSON.stringify

为了避免因为对象是引用类型而造成的数据源污染,我们通常使用 JSON.stringify 将其转换为字符串,而后通过JSON.parse方法将字符串转化一个新对象来实现深拷贝。但是在这个过程中也会存在一些问题,本文就介绍一下使用JSON.stringify可能遇到的一些问题,尽可能在以后避免这些问题。

先看一段代码:

let obj = {name: 'leo',age: Infinity
}
let originObj = JSON.stringify(obj)
console.log(originObj) // {"name":"leo","age":null}

可以看到在转换过程中Infinity变成了null

先看看解决办法:

  1. 简单粗暴,重新赋值age属性
  2. 使用JSON.stringify的第二个参数
function censor(key, value) {if (value === Infinity) {return "Infinity";}return value;
}
const b = JSON.stringify(obj, censor);const c = JSON.parse(b,function (key, value) {return value === "Infinity"  ? Infinity : value;}
);
console.log(c); // {name: 'leo', age: Infinity}

作为参考,大家可能直接用了第一种方法。但是这里可以看到JSON.stringify其实还有第二个参数,那么它有什么用呢?接下来我们就来揭开它的神秘面纱。

JSON.stringify 的基本语法

语法:

JSON.stringify(value[, replacer [, space]])

JSON.stringify() 方法将 JavaScript 对象或值转换为 JSON 字符串,如果指定了替换函数,则可选择替换值;如果指定了替换数组,则可选择仅包含指定的属性。

简单来说,JSON.stringify() 将一个值转换为相应的 JSON 格式的字符串。

参数替换器

也就是第二个参数(replacer),该参数是可选的,可以是函数或数组。

当是函数时,在序列化过程中,每个待序列化的属性都会被函数进行转换处理。

let replacerFun = function (key, value) {console.log(key, value)if (key === 'name') {return undefined}return value
}let myIntro = {name: 'leo',age: 25,like: 'FE'
}console.log(JSON.stringify(myIntro, replacerFun))
// {"age":25,"like":"FE"}

这实际上是一个过滤函数,它利用了JSON.stringify中的特性,如果对象属性值未定义,则在序列化时会被忽略(稍后我们会提到)。

在开始时,replacer 函数将传递一个空字符串作为键值,代表要字符串化的对象。

上面console.log(key, value)输出的值如下:

{name: 'leo', age: 25, like: 'FE'}  // 其实是 '' {name: 'leo', age: 25, like: 'FE'}, 不过''是个空字符
ame leo
age 25
like FE
{"age":25,"like":"FE"}

可见,通过第二个参数,我们可以更加灵活地操作和修改序列化目标的值。

当第二个参数是数组时,只会序列化数组中包含的属性名称:

JSON.stringify (myIntro, [ 'name' ])  // {"name":"leo"}

第三个参数

指定一个空字符串进行缩进,更常见的是指定一个数字,代表几个空格。

console.log(JSON.stringify(myIntro))
console.log(JSON.stringify(myIntro, null, 2))// 输出
// {"name":"leo","age":25,"like":"FE"}
// {
//   "name": "leo",
//   "age": 25,
//   "like": "FE"
// }

JSON.stringify使用场景

判断对象/数组值是否相等

let a = [1,2,3],b = [1,2,3];
JSON.stringify(a) === JSON.stringify(b);// true

localStorage/sessionStorage 存储对象

我们知道localStorage/sessionStorage只能存储字符串。当我们要存储对象时,需要使用 JSON.stringify 将其转换为字符串,然后在获取对象时使用 JSON.parse解析出来。

function setLocalStorage(key,val) {window.localStorage.setItem(key, JSON.stringify(val));
};function getLocalStorage(key) {let val = JSON.parse(window.localStorage.getItem(key));return val;
};

实现对象深拷贝

let myIntro = {name: 'leo',age: 25,like: 'FE'
}function deepClone() {return JSON.parse(JSON.stringify(myIntro))
}let copyMe = deepClone(myIntro)
copyMe.like = 'js only'
console.log(myIntro, copyMe)// { name: 'leo', age: 25, like: 'FE' } { name: 'leo', age: 25, like: 'js only' }

路由(浏览器地址)参数传递

由于浏览器参数只能通过字符串传递,所以还需要JSON.stringify

使用 JSON.stringify 的注意事项

在某些场景下使用 JSON.stringify 可能会引发一些难以发现的问题:

转换属性值中有toJSON方法

如果转换值中有toJSON方法,则该方法返回的值将是最终的序列化结果。

let toJsonMyIntro = {name: "Gopal",age: 25,like: "FE",toJSON: function () {return "frontend";},
};console.log(JSON.stringify(toJsonMyIntro)); // "frontend"

转换后的值中有未定义、任意函数、符号值

分为两种情况:

一种是数组对象,未定义的、任意函数和符号值都会被转换为null

JSON.stringify([undefined, Object, Symbol("")]);
// '[null,null,null]'

一种是非数组对象,序列化时会被忽略。

JSON.stringify({ x: undefined, y: Object, z: Symbol("") });
// '{}'

对于这些情况,我们可以使用JSON.stringify的第二个参数来使其满足我们的期望。

const testObj = { x: undefined, y: Object, z: Symbol("test") }const resut = JSON.stringify(testObj, function (key, value) {if (value === undefined) {return 'undefined'} else if (typeof value === "symbol" || typeof value === "function") {return value.toString()}return value
})console.log(resut)
// {"x":"undefined","y":"function Object() { [native code] }","z":"Symbol(test)"}

含有循环引用的对象

let objA = {name: "leo",
}let objB = {age: 25,
}objA.age = objB
objB.name = objA
JSON.stringify(objA)

上面的代码会报错: VM1140:11 Uncaught TypeError: Converting circular structure to JSON

以symbol为属性键的属性

所有使用符号作为键控的属性都将被完全忽略,即使它们必须包含在替换参数中。

JSON.stringify({ [Symbol.for("foo")]: "foo" }, [Symbol.for("foo")])
// '{}'JSON.stringify({ [Symbol.for("foo")]: "foo" }, function (k, v) {if (typeof k === "symbol") {return "a symbol";}
})
// undefined

值为NaN和Infinity

数组值或具有 NaNInfinity 值的非数组对象属性将转换为 null

let me = {name: "leo",age: Infinity,money: NaN,
};
let originObj = JSON.stringify(me);
console.log(originObj); // {"name":"leo","age":null,"money":null}JSON.stringify([NaN, Infinity])
// [null,null]

具有不可枚举的属性值

默认情况下,不可枚举属性被忽略。

let person = Object.create(null, {name: { value: "leo", enumerable: false },age: { value: "25", enumerable: true },
})console.log(JSON.stringify(person))
// {"age":"25"}

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

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

相关文章

我的 365 天创作纪念日

✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…

【Unity造轮子】实现一个类csgo的武器轮盘功能

文章目录 前言素材导入开始1.放背景和中间的圆圈,调整合适的宽高和位置2.添加选择图像框3.添加一些武器道具选择4.书写脚本RadialMenuManager5.绑定脚本和对象6.运行效果,按tab键开启关闭轮盘7.优化添加显示选中的武器文本8.添加鼠标选中放大的效果9.添加…

MySQL和Oracle区别

由于SQL Server不常用,所以这里只针对MySQL数据库和Oracle数据库的区别 (1) 对事务的提交 MySQL默认是自动提交,而Oracle默认不自动提交,需要用户手动提交,需要在写commit;指令或者点击commit按钮 (2) 分页查询 MySQL是直接在SQL…

JIT 与 C#热更

JIT与AOT 一般程序运行有两种方式,静态编译与动态编译。 AOT: Ahead Of Time,预先(静态)编译 静态编译的程序,需要在执行之前全部翻译为机器码,运行前会使得程序安装时间相对较长,但程序运行的时候&#…

自然语言处理从入门到应用——LangChain:提示(Prompts)-[提示模板:基础知识]

分类目录:《自然语言处理从入门到应用》总目录 语言模型以文本作为输入,这段文本通常被称为提示(Prompt)。通常情况下,这不仅仅是一个硬编码的字符串,而是模板、示例和用户输入的组合。LangChain提供了多个…

加载已训练好的目标检测YOLOv8,v5,v3,v6模型,对数据集中某张图片中的object打上方框、标出类别,并将图片保存到本地

参考的教程:Python - Ultralytics YOLOv8 Docs 在与ultralytics代码同一层级下新建 predict.py 里面写下面的内容。运行即可 from ultralytics import YOLO from PIL import Image import cv2# 加载计划使用的模型 model YOLO("yolov8n.pt") # load a…

SQL力扣练习(九)

目录 1.订单最多的用户(586) 示例 1 解法一(limit) 解法二(dense_rank()) 2.体育馆的人流量 示例 1 解法一(临时表) 解法二(三表法) 1.订单最多的用户(586) 表: Orders --------------------------- | Column Name | Type | ---------…

2023年的深度学习入门指南(24) - 处理音频的大模型 OpenAI Whisper

2023年的深度学习入门指南(24) - 处理音频的大模型 OpenAI Whisper 在这一讲之前,我们所用的大模型都是针对文本的。这一讲我们增加一个新的领域,即音频。我们将介绍OpenAI的Whisper模型,它是一个处理音频的大模型。 Whisper模型的用法 Wh…

Qt信号与槽机制的本质

引入 对象与对象之间的通信有多个方式,如果我们要提供一种对象之间的通信机制。这种机制,要能够给两个不同对象中的函数建立映射关系,前者被调用时后者也能被自动调用。 再深入一些,两个对象如果都互相不知道对方的存在&#xff…

leetcode每日一练-第102题-二叉树的层序遍历

一、思路 BFS 二、解题方法 通过广度优先搜索(BFS)的方式,按层遍历二叉树节点,并将每层的节点值保存在一个一维数组中,然后再将所有的一维数组存储在二维数组中,最后返回二维数组作为层序遍历的结果。 …

三菱plcCCLINK转profinet与西门子PLC通讯案例分析

用三菱PLC的控制系统需要和西门子的PLC控制系统交互数据,捷米JM-PN-CCLK 是自主研发的一款 PROFINET 从站功能的通讯网关。该产品主要功能是将各种 CCLINK 总线和 PROFINET 网络连接起来。 捷米JM-PN-CCLK总线中做为从站使用,连接到 CCLINK 总线中做为…

地产变革中,物业等风来

2023年7月,也许是中国房地产行业变局中的一个大拐点。 中信建投研报表示,政治局会议指出当前我国房地产形势已发生重大变化,要适时调整优化政策,为行业形势定调……当前房地产行业β已至。 不久前,国家统计局公布了2…

洞悉安全现状,建设网络安全防护新体系

一、“网络攻防演练行动“介绍 国家在2016年发布《网络安全法》,出台网络安全攻防演练相关规定:关键信息基础设施的运营者应“制定网络安全事件应急预案,并定期进行演练”。同年“实战化网络攻防演练行动”成为惯例。由公安部牵头&#xff0…

Monorepo

❌现有的架构可能会遇到什么问题 分散的git仓库 随着时间的沉淀,项目数量在飞速增长,增加了项目的工程管理难度 怎么区分来源呢? 通过git目录划分通过项目命名 新员工入职的时候需要,需要下载代码并安装所需的依赖。在这种情…

计算机毕设 深度学习实现行人重识别 - python opencv yolo Reid

文章目录 0 前言1 课题背景2 效果展示3 行人检测4 行人重识别5 其他工具6 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉…

【技术讨论】RF环境搭建手册

简要整理下环境搭建的步骤,以便快速、准确的搭建测试环境。 一、环境搭建 一、Python 2.7 1、 不要用Python3.6,很多库3.6中还没有,wxPython官方只支持Python 2。 2、 环境变量配置后需要重启才能生效。 3、 环境变量添加C:\Python27\Sc…

一文详解:自动化测试工具——Selenium

前言 Selenium是一个用于Web应用程序测试的工具。是一个开源的Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium可以直接运行在浏览器上,…

明明已经安装字体,但IDEA、CLION无法找到思源黑体/Source Hans Sans的问题解决

IDEA、CLION的Jetbrain系列软件不支持非TrueType的中文字体,而Adobe官方给出的字体却不是TrueType的,所以便会导致Jetbrain系软件无法找到已安装的中文字体,因此我们需要安装TrueType的字体 请在以下Github链接中下载: TrueType思…

HDFS中数据迁移的使用场景和考量因素

HDFS中数据迁移的使用场景和考量因素 数据迁移使用场景数据迁移要素考量HDFS分布式拷贝工具-DistCpdistcp的优势性能命令 数据迁移使用场景 冷热集群数据同步、分类存储集群数据整体搬迁 当公司业务迅速的发展,导致的当前的服务器数量资源出现临时紧张的时候&#…

python-网络爬虫.Request

Request python中requests库使用方法详解: 一简介: Requests 是Python语言编写,基于urllib, 采用Apache2 Licensed开源协议的 HTTP 库。 与urllib相比,Requests更加方便,处理URL资源特别流畅。 可以节约我…