谨慎使用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。
先看看解决办法:
- 简单粗暴,重新赋值
age属性 - 使用
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
数组值或具有 NaN 和 Infinity 值的非数组对象属性将转换为 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"}
相关文章:
谨慎使用JSON.stringify
谨慎使用JSON.stringify 为了避免因为对象是引用类型而造成的数据源污染,我们通常使用 JSON.stringify 将其转换为字符串,而后通过JSON.parse方法将字符串转化一个新对象来实现深拷贝。但是在这个过程中也会存在一些问题,本文就介绍一下使用…...
驱动开发day8
编写LED灯的驱动,使用GPIO子系统,里面添加按键的中断处理 1.应用程序发送指令控制LED亮灭 2.按键1 按下,led1电位反转 按键2按下,led2电位反转 按键3 按下,led3电位反转 驱动程序 #include <linux/init.h> #i…...
CAS 机制
问题分析与思考: CAS 是 Java 中 Unsafe 类里面的方法,它的全称是 CompareAndSwap,比较并交换 的意思。 它的主要功能是能够保证在多线程环境下,对于共享变量的修改的原子性。 举个例子,比如说有这样一个场景ÿ…...
#P1003. [NOIP2009普及组] 道路游戏
题目描述 小新正在玩一个简单的电脑游戏。 游戏中有一条环形马路,马路上有 nn 个机器人工厂,两个相邻机器人工厂之间由一小段马路连接。小新以某个机器人工厂为起点,按顺时针顺序依次将这 nn 个机器人工厂编号为 1\sim n1∼n,因…...
python-网络爬虫.regular
regular 正则表达式 (regular expression) 正则表达式(regular expression)描述了一种字符串匹配的模式 (pattern), 可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串 中取出符合某个条件的子串等。 正则表达式是由普通…...
手动搭建gateway,项目集成gateway实现Token效果
目录 背景步骤1、首先创建springboot项目2、引入依赖3、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)4、相关类我们在服务中进行的白名单中接口的操作如…...
linux下SVN服务器搭建
在本教程中,我们将介绍如何在Linux系统下搭建Subversion(SVN)服务器。Subversion是一种流行的版本控制系统,它允许多个人在同一项目上进行协作,同时避免了他们各自的更改发生冲突。 安装SVN 在大多数Linux发行版中&am…...
技术等级 TRL 定义
“不同环境、不同目标下TRL表述不一样” 技术等级 TRL 定义 TRL1 基本原理提出和发现 TRL2 技术应用研究 TRL3 完成概念验证,如叶栅试验、燃烧室头部试验等 TRL4 完成模拟部件试验.如压气机性能试验,燃烧室扇形试验 TRL5 完…...
DHorse v1.3.0 发布,基于k8s的发布平台
综述 DHorse是一个简单易用、以应用为中心的云原生DevOps系统,具有持续集成、持续部署、微服务治理等功能,无需安装依赖Docker、Maven、Node等环境即可发布Java、Vue、React应用,主要特点:部署简单、操作简洁、功能快速。 新增特…...
Redis - 缓存的双写一致性
概念: 当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致 那为什么会有不一致的情况呢? 如果不追求一致性,正常有两种做法 先修改数据库 后删除旧的缓存先删除旧的缓存 再修改数据库 我们以先删除旧的…...
opencv03-Mat矩阵API的使用
opencv03-Mat矩阵API的使用 构造方法(具体介绍看API文档) int main() {Mat m1 Mat(200, 100, CV_8UC1);imshow("o1", m1);Mat m2 Mat(Size(100, 200), CV_8UC1);imshow("o2", m2);Mat m3 Mat(200, 100, CV_8UC3, Scalar(255, 0, 0));imshow("o3&…...
2023届浙江大学MPA提面A资格经验总结分享
本人是去年报考的浙大MPA项目,并通过提面获得了A资格,新一年浙大MPA项目提前批面试已经开始了,受达立易考周老师邀请来分享下我的提面经验,希望我的经验能对还在迷茫中的小伙伴有所帮助。 点开提面通知,首先看到…...
BugKu CTF(杂项篇MISC)—想要种子吗
BugKu CTF(杂项篇MISC)—想要种子吗 提 示: 描 述:flag{} 题目下载后是一张图片,打开如下。 一、工具 十六进制编辑器010 editor kali系统文件分离工具binwalk或者foremost 维吉尼亚密码 STEGHIDE图片隐写工具 文章所需的软件下载地址 ARCHPR压缩包密码破解…...
类之间的关系
1、类关系 继承、实现、依赖、组合、聚合 继承:一个类继承另一个类; 实现:一个类实现另一个接口; 依赖:一个类作为另一个的局部变量,方法的参数,临时对象等; 组合:一个类…...
【蓝图】p40-p43对象引用、变量有效性、实现键盘控制物体自转、简单点名系统
p40-p43对象引用、变量有效性、实现键盘控制物体自转、简单点名系统 p40对象引用、变量有效性p41实现键盘控制物体自转创建bool值控制旋转实现通过键盘控制自转 p42p43简单点名系统Get All Actors Of Class(获得场景中所有该类的actor演员)getFor Each L…...
vscode设置远程登录和免密登录
首先,我们去官网下载VScode 安装过程比较简单,大家自行安装即可,注意建议安装在除C盘外的其他盘中。 安装完成后,打开我们下载好的VScode,点击左侧的Extensions选项,搜索Remote,Install第一项R…...
今日头条面试真题及答案,软件测试工程师面试秘籍
试题1.在浏览器地址栏里输入一个网址,接下来会发生什么? 答案:发生的操作如下。 (1)浏览器查找该网址的IP地址。 (2)浏览器根据解析得到的IP地址向Web服务器发送一个HTTP请求。 &am…...
JavaScript Windows 浏览器对象模型
Window 对象 BOM 的核心就是 window 对象所有浏览器都支持 window 对象。它表示浏览器窗口。所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。全局变量是 window 对象的属性。全局函数是 window 对象的方法。HTML DOM 的 document 也是 window 对象的属…...
【uniapp 获取缓存及清除缓存】
小程序及H5 获取缓存: 使用uniapp中的wx.getStorageInfoSync()方法可以获取当前小程序或H5应用的本地缓存信息,如下所示: let storageInfo uni.getStorageInfoSync() console.log(storageInfo)其中,storageInfo是一个对象&…...
【vim 学习系列文章 2 - vim 常用插件配置】
文章目录 1.1 vim 常用插件1.1.1 vim 插件 Pathogen 管理1.1.2 vim 常用插件推荐1.1.3 vim Leaderf1.1.4 vim ripgrep 工具1.1.5 vim Leaderf 配合 rg1.1.6 vim autocmd 配置 1.2 其它类型文件 vimrc 配置1.2.1 System Verilog vimrc 配置 上篇文章:vim 学习系列文章…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
