带你了解JSON.stringify,看看怎么使用

你真的会使用强大的json.stringify方法吗?下面本篇文章带大家详细了解解json.stringify,介绍一下使用方法,希望对大家有所帮助!

带你了解JSON.stringify,看看怎么使用

JSON.stringify 作为日常开发中经常使用的方法,你真的能灵活运用它吗?

学习本文之前,小包想让大家带着几个问题,一起来深入学习 stringify 。【相关推荐:javascript视频教程】

stringify 函数有几个参数,每个参数分别有啥用啊?stringify 序列化准则有哪些啊?函数序列化中会如何处理?null、undefined、NaN 等特殊的值又会如何处理?ES6 后增加的 Symbol 类型、BigInt 序列化过程中会有特别处理吗?stringify 为什么不适合做深拷贝?你能想到那些 stringify 的妙用?

整个文章的脉络跟下面思维导图一致,大家可以先留一下印象。

1.png

三参数

在日常编程中,我们经常 JSON.stringify 方法将某个对象转换成 JSON 字符串形式。

const stu = {    name: 'zcxiaobao',    age: 18}// {"name":"zcxiaobao","age":18}console.log(JSON.stringify(stu));

登录后复制

但 stringify 真的就这么简单吗?我们先来看一下 MDN 中对 stringify 的定义。

MDN 中指出: JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串,如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性。

看完定义,小包就一惊,stringfy 不止一个参数吗?当然了,stringify 有三个参数。

咱们来看一下 stringify 语法和参数介绍:

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

登录后复制value: 将要序列后成 JSON 字符串的值。replacer(可选)

如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;

如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中

如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。

space(可选): 指定缩进用的空白字符串,用于美化输出

如果参数是个数字,它代表有多少的空格。上限为10。

该值若小于1,则意味着没有空格

如果该参数为字符串(当字符串长度超过10个字母,取其前10个字母),该字符串将被作为空格

如果该参数没有提供(或者为 null),将没有空格

replacer

我们来尝试一下 replacer 的使用。

replacer 作为函数

replacer 作为函数,它有两个参数,键(key) 和 值(value),并且两个参数都会被序列化。

在开始时,replacer 函数会被传入一个空字符串作为 key 值,代表着要被 stringify 的这个对象。理解这点很重要,replacer 函数并非是上来就把对象解析成键值对形式,而是先传入了待序列化对象。随后每个对象或数组上的属性会被依次传入。 如果函数返回值为undefined或者函数时,该属性值会被过滤掉,其余按照返回规则。

// repalcer 接受两个参数 key value// key value 分别为对象的每个键值对// 因此我们可以根据键或者值的类型进行简单筛选function replacer(key, value) {  if (typeof value === "string") {    return undefined;  }  return value;}// function 可自己测试function replacerFunc(key, value) {  if (typeof value === "string") {    return () => {};  }  return value;}const foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7};const jsonString = JSON.stringify(foo, replacer);

登录后复制

JSON 序列化结果为 {“week”:45,”month”:7}

但如果序列化的是数组,若 replacer 函数返回 undefined 或者函数,当前值不会被忽略,而将会被 null 取代。

const list = [1, '22', 3]const jsonString = JSON.stringify(list, replacer)

登录后复制

JSON 序列化的结果为 ‘[1,null,3]’

replacer 作为数组

作为数组比较好理解,过滤数组中出现的键值。

const foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7};const jsonString = JSON.stringify(foo, ['week', 'month']);

登录后复制

JSON 序列化结果为 {“week”:45,”month”:7}, 只保留 week 和 month 属性值。

九特性

特性一: undefined、函数、Symbol值

出现在非数组对象属性值中: undefined、任意函数、Symbol 值在序列化过程中将会被忽略

出现在数组中: undefined、任意函数、Symbol值会被转化为 null

单独转换时: 会返回 undefined

// 1. 对象属性值中存在这三种值会被忽略const obj = {  name: 'zc',  age: 18,  // 函数会被忽略  sayHello() {    console.log('hello world')  },  // undefined会被忽略  wife: undefined,  // Symbol值会被忽略  id: Symbol(111),  // [Symbol('zc')]: 'zc',}// 输出结果: {"name":"zc","age":18}console.log(JSON.stringify(obj));// 2. 数组中这三种值会被转化为 nullconst list = [  'zc',   18,   // 函数转化为 null  function sayHello() {    console.log('hello world')  },   // undefined 转换为 null  undefined,   // Symbol 转换为 null  Symbol(111)]// ["zc",18,null,null,null]console.log(JSON.stringify(list))// 3. 这三种值单独转化将会返回 undefinedconsole.log(JSON.stringify(undefined))  // undefinedconsole.log(JSON.stringify(Symbol(111))) // undefinedconsole.log(JSON.stringify(function sayHello() {   console.log('hello world')})) // undefined

登录后复制

特性二: toJSON() 方法

转换值如果有 toJSON() 方法,toJSON() 方法返回什么值,序列化结果就返回什么值,其余值会被忽略。

const obj = {  name: 'zc',  toJSON(){    return 'return toJSON'  }}// return toJSONconsole.log(JSON.stringify(obj));

登录后复制

特性三: 布尔值、数字、字符串的包装对象

布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值

JSON.stringify([new Number(1), new String("zcxiaobao"), new Boolean(true)]);// [1,"zcxiaobao",true]

登录后复制

特性四: NaN Infinity null

特性四主要针对 JavaScript 里面的特殊值,例如 Number 类型里的 NaN 和 Infinity 及 null 。此三种数值序列化过程中都会被当做 null

// [null,null,null,null,null]JSON.stringify([null, NaN, -NaN, Infinity, -Infinity])// 特性三讲过布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值// 隐式类型转换就会调用包装类,因此会先调用 Number => NaN// 之后再转化为 null// 1/0 => Infinity => nullJSON.stringify([Number('123a'), +'123a', 1/0])

登录后复制

特性五: Date对象

Date 对象上部署了 toJSON 方法(同 Date.toISOString())将其转换为字符串,因此 JSON.stringify() 将会序列化 Date 的值为时间格式字符串

// "2022-03-06T08:24:56.138Z"JSON.stringify(new Date())

登录后复制

特性六: Symbol

特性一提到,Symbol 类型当作值来使用时,对象、数组、单独使用分别会被忽略、转换为 null 、转化为 undefined。

同样的,所有以 Symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们

const obj = {  name: 'zcxiaobao',  age: 18,  [Symbol('lyl')]: 'unique'}function replacer(key, value) {  if (typeof key === 'symbol') {    return value;  }}// undefinedJSON.stringify(obj, replacer);

登录后复制

通过上面案例,我们可以看出,虽然我们通过 replacer 强行指定了返回 Symbol 类型值,但最终还是会被忽略掉。

特性七: BigInt

JSON.stringify 规定: 尝试去转换 BigInt 类型的值会抛出 TypeError

const bigNumber = BigInt(1)// Uncaught TypeError: Do not know how to serialize a BigIntconsole.log(JSON.stringify(bigNumber))

登录后复制

特性八: 循环引用

特性八指出: 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误

日常开发中深拷贝最简单暴力的方式就是使用 JSON.parse(JSON.stringify(obj)),但此方法下的深拷贝存在巨坑,关键问题就在于 stringify 无法处理循环引用问题。

const obj = {  name: 'zcxiaobao',  age: 18,}const loopObj = {  obj}// 形成循环引用obj.loopObj = loopObj;JSON.stringify(obj)/* Uncaught TypeError: Converting circular structure to JSON    --> starting at object with constructor 'Object'    |     property 'loopObj' -> object with constructor 'Object'    --- property 'obj' closes the circle    at JSON.stringify ()    at :10:6*/

登录后复制

特性九: 可枚举属性

对于对象(包括 Map/Set/WeakMap/WeakSet)的序列化,除了上文讲到的一些情况,stringify 也明确规定,仅会序列化可枚举的属性

// 不可枚举的属性默认会被忽略// {"age":18}JSON.stringify(    Object.create(        null,        {            name: { value: 'zcxiaobao', enumerable: false },            age: { value: 18, enumerable: true }        }    ));

登录后复制

六妙用

localStorage

localStorage 对象用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。通常我们以对象形式进行存储。

单纯调用 localStorage 对象方法

const obj = {  name: 'zcxiaobao',  age: 18}// 单纯调用 localStorage.setItem()localStorage.setItem('zc', obj);// 最终返回结果是 [object Object]// 可见单纯调用localStorage是失败的console.log(localStorage.getItem('zc'))

登录后复制

localStorage 配合 JSON.stringify 方法

localStorage.setItem('zc', JSON.stringify(obj));// 最终返回结果是 {name: 'zcxiaobao', age: 18}console.log(JSON.parse(localStorage.getItem('zc')))

登录后复制

属性过滤

来假设这样一个场景,后端返回了一个很长的对象,对象里面属性很多,而我们只需要其中几个属性,并且这几个属性我们要存储到 localStorage 中。

方案一: 解构赋值+ stringify

// 我们只需要 a,e,f 属性const obj = {  a:1, b:2, c:3, d:4, e:5, f:6, g:7}// 解构赋值const {a,e,f} = obj;// 存储到localStoragelocalStorage.setItem('zc', JSON.stringify({a,e,f}))// {"a":1,"e":5,"f":6}console.log(localStorage.getItem('zc'))

登录后复制

使用 stringify 的 replacer 参数

// 借助 replacer 作为数组形式进行过滤localStorage.setItem('zc', JSON.stringify(obj, ['a','e','f']))// {"a":1,"e":5,"f":6}console.log(localStorage.getItem('zc'))

登录后复制

当 replacer 是数组时,可以简单的过滤出我们所需的属性,是一个不错的小技巧。

三思而后行之深拷贝

使用 JSON.parse(JSON.stringify) 是实现对象的深拷贝最简单暴力的方法之一。但也正如标题所言,使用该种方法的深拷贝要深思熟虑。

循环引用问题,stringify 会报错

函数、undefined、Symbol 会被忽略

NaN、Infinity 和 -Infinity 会被序列化成 null

因此在使用 JSON.parse(JSON.stringify) 做深拷贝时,一定要深思熟虑。如果没有上述隐患,JSON.parse(JSON.stringify) 是一个可行的深拷贝方案。

对象的 map 函数

在使用数组进行编程时,我们会经常使用到 map 函数。有了 replacer 参数后,我们就可以借助此参数,实现对象的 map 函数。

const ObjectMap = (obj, fn) => {  if (typeof fn !== "function") {    throw new TypeError(`${fn} is not a function !`);  }  // 先调用 JSON.stringify(obj, replacer) 实现 map 功能  // 然后调用 JSON.parse 重新转化成对象  return JSON.parse(JSON.stringify(obj, fn));};// 例如下面给 obj 对象的属性值乘以2const obj = {  a: 1,  b: 2,  c: 3}console.log(ObjectMap(obj, (key, val) => {  if (typeof value === "number") {    return value * 2;  }  return value;}))

登录后复制

很多同学有可能会很奇怪,为什么里面还需要多加一部判断,直接 return value * 2 不可吗?

上文讲过,replacer 函数首先传入的是待序列化对象,对象 * 2 => NaN => toJSON(NaN) => undefined => 被忽略,就没有后续的键值对解析了。

删除对象属性

借助 replacer 函数,我们还可以删除对象的某些属性。

const obj = {  name: 'zcxiaobao',  age: 18}// {"age":18}JSON.stringify(obj, (key, val) => {  // 返回值为 undefined时,该属性会被忽略   if (key === 'name') {    return undefined;  }  return val;})

登录后复制

对象判断

JSON.stringify 可以将对象序列化为字符串,因此我们可以借助字符串的方法来实现简单的对象相等判断。

//判断数组是否包含某对象const names = [  {name:'zcxiaobao'},  {name:'txtx'},  {name:'mymy'},];const zcxiaobao = {name:'zcxiaobao'};// trueJSON.stringify(names).includes(JSON.stringify(zcxiaobao)) // 判断对象是否相等const d1 = {type: 'div'}const d2 = {type: 'div'}// trueJSON.stringify(d1) === JSON.stringify(d2);

登录后复制

数组对象去重

借助上面的思想,我们还能实现简单的数组对象去重。

但由于 JSON.stringify 序列化 {x:1, y:1} 和 {y:1, x:1} 结果不同,因此在开始之前我们需要处理一下数组中的对象。

方法一: 将数组中的每个对象的键按字典序排列

arr.forEach(item => {  const newItem = {};  Object.keys(item)   // 获取对象键值        .sort()       // 键值排序        .map(key => { // 生成新对象          newItem[key] = item[key];        })  // 使用 newItem 进行去重操作})

登录后复制

但方法一有些繁琐,JSON.stringify 提供了 replacer 数组格式参数,可以过滤数组。

方法二: 借助 replacer 数组格式

function unique(arr) {  const keySet = new Set();  const uniqueObj = {}  // 提取所有的键  arr.forEach(item => {    Object.keys(item).forEach(key => keySet.add(key))  })  const replacer = [...keySet];  arr.forEach(item => {    // 所有的对象按照规定键值 replacer 过滤    unique[JSON.stringify(item, replacer)] = item;  })  return Object.keys(unique).map(u => JSON.parse(u))}// 测试一下unique([{}, {},       {x:1},      {x:1},      {a:1},      {x:1,a:1},      {x:1,a:1},      {x:1,a:1,b:1}      ])// 返回结果[{},{"x":1},{"a":1},{"x":1,"a":1},{"x":1,"a":1,"b":1}]

登录后复制

【相关推荐:web前端】

以上就是带你了解JSON.stringify,看看怎么使用的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2702293.html

(0)
上一篇 2025年3月7日 19:22:38
下一篇 2025年3月6日 13:46:22

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 聊聊 typeof 和 instanceof 间有什么区别

    typeof和instanceof操作符都可用来判断数据类型,那么它们之间有什么差异?下面本篇文章就来带大家了解 typeof 和 instanceof ,聊聊它们的区别,希望对大家有所帮助! typeof和instanceof操作符都是用…

    2025年3月7日
    200
  • 图文详解JavaScript原型链

    本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了关于原型链的相关问题,原型链指一些原型通过__proto__指针构成的链表,一个原型链可以为想共享原型链中数据的对象服务,希望对大家有帮助。 相关推荐:javascrip…

    2025年3月7日 编程技术
    200
  • JavaScript中如何获取URL参数?4种常见方法详解

    如何利用原生javascript来获取url链接参数?下面本篇文章给大家详细介绍4种常见的原生js方法,希望对大家有所帮助! 作为一个前端开发,我们很多时候都需要对URL进行操作和处理,最常见的一种就是获取URL链接中携带的参数值了。使用框…

    2025年3月7日 编程技术
    200
  • 带你了解JavaScript解构赋值

    本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了关于解构赋值的相关问题,包括了数组解构、对象结构以及解构的用途等等,希望对大家有帮助。 相关推荐:javascript学习教程 概念 ES6提供了更简洁的赋值模式,从数组…

    2025年3月7日
    200
  • javascript数据类型学习之浅析Symbol类型

    本篇文章带大家了解一下es6新增的数据类型:symbol,聊聊symbol类型的声明方式和使用方法,希望对大家有所帮助! Symbol一种新的原始数据类型,表示独一无二的。是javascript中第七种数据类型。另外六种分别是:undefi…

    2025年3月7日
    200
  • 帮你搞定JavaScript对象

    本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了关于对象的相关问题,包括了构造函数和各种内置对象的知识点,希望对大家有帮助。 相关推荐:javascript教程 一、构造函数 为什么使用构造函数 什么是构造函数:是Ja…

    2025年3月7日 编程技术
    200
  • 一文带你深入了解React Hooks!

    react中你了解hooks吗?本篇文章带大家来深入了解下react hooks,希望对大家有所帮助! 前言 Hooks 用于在不编写 class 的情况下,使用 state 以及其他 React 特性。那么 Hooks 究竟是什么,为什么…

    2025年3月7日
    200
  • JavaScript正则表达式,这一篇就够了

    本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了正则表达式的相关问题,正则表达式是一个特定的格式化模式,用于验证各种字符串是否匹配这个特征,进而实现高级的文本查找、替换、截取内容等操作,希望对大家有帮助。 相关推荐:j…

    2025年3月7日 编程技术
    200
  • 为什么会有Symbol类型?怎么使用?

    什么是 symbol?为什么会有这么个东西?下面本篇文章给大家介绍一下javascript中的symbol类型,聊聊使用方法,希望对大家有所帮助! 什么是 Symbol?为什么会有这么个东西? Symbol(符号)是 ES6 新增的数据类型…

    2025年3月7日
    200
  • 一起聊聊JavaScript函数式编程

    本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了函数式编程的相关问题,函数式编程可以理解为,以函数作为主要载体的编程方式,用函数去拆解、抽象一般的表达式,希望对大家有帮助。 相关推荐:javascript学习教程 看过…

    2025年3月7日
    200

发表回复

登录后才能评论