当前位置: 首页 > news >正文

谨慎使用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.lryc.cn/news/104566.html

相关文章:

  • 驱动开发day8
  • CAS 机制
  • #P1003. [NOIP2009普及组] 道路游戏
  • python-网络爬虫.regular
  • 手动搭建gateway,项目集成gateway实现Token效果
  • linux下SVN服务器搭建
  • 技术等级 TRL 定义
  • DHorse v1.3.0 发布,基于k8s的发布平台
  • Redis - 缓存的双写一致性
  • opencv03-Mat矩阵API的使用
  • 2023届浙江大学MPA提面A资格经验总结分享
  • BugKu CTF(杂项篇MISC)—想要种子吗
  • 类之间的关系
  • 【蓝图】p40-p43对象引用、变量有效性、实现键盘控制物体自转、简单点名系统
  • vscode设置远程登录和免密登录
  • 今日头条面试真题及答案,软件测试工程师面试秘籍
  • JavaScript Windows 浏览器对象模型
  • 【uniapp 获取缓存及清除缓存】
  • 【vim 学习系列文章 2 - vim 常用插件配置】
  • 【外卖系统】修改菜品
  • 【暑期每日一练】 day11
  • 神经概率语言模型
  • 什么是shadow DOM?
  • 我的 365 天创作纪念日
  • spark-sql : “java.lang.NoSuchFieldError: out“ 异常解决
  • Node.js入门笔记(包含源代码)以及详细解析
  • windows自动化点击大麦app抢购、捡漏,仅支持windows11操作系统
  • vue 拦截 v-html 中 a 标签 href 跳转
  • 分布式id、系统id、业务id以及主键之间的关系
  • 设计模式七:适配器模式(Adapter Pattern)