一、JSON.stringify介绍

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

语法:

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

参数:

value 将要序列化成 一个 JSON 字符串的值。

replacer 可选

  • 如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;
  • 如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;
  • 如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。

space 可选

  • 指定缩进用的空白字符串,用于美化输出(pretty-print);
  • 如果参数是个数字,它代表有多少的空格;上限为10。
  • 该值若小于1,则意味着没有空格;
  • 如果该参数为字符串(当字符串长度超过10个字母,取其前10个字母),该字符串将被作为空格;
  • 如果该参数没有提供(或者为 null),将没有空格。

返回值:

一个表示给定值的JSON字符串。

异常:

  • 当在循环引用时会抛出异常TypeError ("cyclic object value")(循环对象值)
  • 当尝试去转换 BigInt 类型的值会抛出TypeError ("BigInt value can't be serialized in JSON")(BigInt值不能JSON序列化).

基本使用:

注意:

  • JSON.stringify可以转换对象或者值(平常用的更多的是转换对象)
  • 可以指定replacer为函数选择性的地替换
  • 也可以指定replacer为数组,可转换指定的属性

这里仅仅是MDN上关于JSON.stringify其中最基础的说明,咱们先打个码试试这几个特性

1. 转换对象

console.log(JSON.stringify({ name: '前端胖头鱼', sex: 'boy' })) // '{"name":"前端胖头鱼","sex":"boy"}'

 2. 转换普通值

console.log(JSON.stringify('前端胖头鱼')) // "前端胖头鱼"
console.log(JSON.stringify(1)) // "1"
console.log(JSON.stringify(true)) // "true"
console.log(JSON.stringify(null)) // "null"

3. 指定replacer函数

console.log(JSON.stringify({ name: '前端胖头鱼', sex: 'boy', age: 100 }, (key, value) => {
  return typeof value === 'number' ? undefined : value
}))
// '{"name":"前端胖头鱼","sex":"boy"}'

4. 指定数组

console.log(JSON.stringify({ name: '前端胖头鱼', sex: 'boy', age: 100 }, [ 'name' ]))
// '{"name":"前端胖头鱼"}'

 5. 指定space(美化输出)

console.log(JSON.stringify({ name: '前端胖头鱼', sex: 'boy', age: 100 }))
// '{"name":"前端胖头鱼","sex":"boy","age":100}'
console.log(JSON.stringify({ name: '前端胖头鱼', sex: 'boy', age: 100 }, null , 2))
/*
{
  "name": "前端胖头鱼",
  "sex": "boy",
  "age": 100
}
*/

二、9大特性要记住

以前仅仅是使用了这个方法,却没有详细了解他的转换规则,居然有9个之多。

特性一

undefined、任意的函数以及symbol值,出现在非数组对象的属性值中时在序列化过程中会被忽略
undefined、任意的函数以及symbol值出现在数组中时会被转换成 null。
undefined、任意的函数以及symbol值被单独转换时,会返回 undefined

// 1. 对象中存在这三种值会被忽略
console.log(JSON.stringify({
  name: '前端胖头鱼',
  sex: 'boy',
  // 函数会被忽略
  showName () {
    console.log('前端胖头鱼')
  },
  // undefined会被忽略
  age: undefined,
  // Symbol会被忽略
  symbolName: Symbol('前端胖头鱼')
}))
// '{"name":"前端胖头鱼","sex":"boy"}'

// 2. 数组中存在着三种值会被转化为null
console.log(JSON.stringify([
  '前端胖头鱼',
  'boy',
  // 函数会被转化为null
  function showName () {
    console.log('前端胖头鱼')
  },
  //undefined会被转化为null
  undefined,
  //Symbol会被转化为null
  Symbol('前端胖头鱼')
]))
// '["前端胖头鱼","boy",null,null,null]'

// 3.单独转换会返回undefined
console.log(JSON.stringify(
  function showName () {
    console.log('前端胖头鱼')
  }
)) // undefined
console.log(JSON.stringify(undefined)) // undefined
console.log(JSON.stringify(Symbol('前端胖头鱼'))) // undefined

特性二

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

console.log(JSON.stringify([new Number(1), new String("前端胖头鱼"), new Boolean(false)]))
// '[1,"前端胖头鱼",false]'

特性三

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

console.log(JSON.stringify({
  [Symbol('前端胖头鱼')]: '前端胖头鱼'}
)) 
// '{}'
console.log(JSON.stringify({
  [ Symbol('前端胖头鱼') ]: '前端胖头鱼',
}, (key, value) => {
  if (typeof key === 'symbol') {
    return value
  }
}))
// undefined

特性四

NaNInfinity 格式的数值及 null 都会被当做 null。

console.log(JSON.stringify({
  age: NaN,
  age2: Infinity,
  name: null
}))
// '{"age":null,"age2":null,"name":null}'

特性五

转换值如果有toJSON() 方法,该方法定义什么值将被序列化。

const toJSONObj = {
  name: '前端胖头鱼',
  toJSON () {
    return 'JSON.stringify'
  }
}

console.log(JSON.stringify(toJSONObj))
// "JSON.stringify"

特性六

Date 日期调用了 toJSON() 将其转换为了 string 字符串(同Date.toISOString()),因此会被当做字符串处理。

const d = new Date()

console.log(d.toJSON()) // 2021-10-05T14:01:23.932Z
console.log(JSON.stringify(d)) // "2021-10-05T14:01:23.932Z"

特性七

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

let cyclicObj = {
  name: '前端胖头鱼',
}

cyclicObj.obj = cyclicObj

console.log(JSON.stringify(cyclicObj))
// Converting circular structure to JSON

特性八

其他类型的对象,包括Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性

let enumerableObj = {}

Object.defineProperties(enumerableObj, {
  name: {
    value: '前端胖头鱼',
    enumerable: true
  },
  sex: {
    value: 'boy',
    enumerable: false
  },
})

console.log(JSON.stringify(enumerableObj))
// '{"name":"前端胖头鱼"}'

特性九

当尝试去转换 BigInt 类型的值会抛出错误

const alsoHuge = BigInt(9007199254740991)

console.log(JSON.stringify(alsoHuge))
// TypeError: Do not know how to serialize a BigInt

三、手写一个JSON.stringify

终于重新学完JSON.stringify的众多特性啦!咱们根据这些特性来手写一个简单版本的吧(无replacer函数和space)

源码实现:

const jsonstringify = (data) => {
  // 确认一个对象是否存在循环引用
  const isCyclic = (obj) => {
  // 使用Set数据类型来存储已经检测过的对象
  let stackSet = new Set()
  let detected = false

  const detect = (obj) => {
    // 不是对象类型的话,可以直接跳过
    if (obj && typeof obj != 'object') {
      return
    }
    // 当要检查的对象已经存在于stackSet中时,表示存在循环引用
    if (stackSet.has(obj)) {
      return detected = true
    }
    // 将当前obj存如stackSet
    stackSet.add(obj)

    for (let key in obj) {
      // 对obj下的属性进行挨个检测
      if (obj.hasOwnProperty(key)) {
        detect(obj[key])
      }
    }
    // 平级检测完成之后,将当前对象删除,防止误判
    /*
      例如:对象的属性指向同一引用,如果不删除的话,会被认为是循环引用
      let tempObj = {
        name: '前端胖头鱼'
      }
      let obj4 = {
        obj1: tempObj,
        obj2: tempObj
      }
    */
    stackSet.delete(obj)
  }

  detect(obj)

  return detected
}

  // 特性七:
  // 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  if (isCyclic(data)) {
    throw new TypeError('Converting circular structure to JSON')
  }

  // 特性九:
  // 当尝试去转换 BigInt 类型的值会抛出错误
  if (typeof data === 'bigint') {
    throw new TypeError('Do not know how to serialize a BigInt')
  }

  const type = typeof data
  const commonKeys1 = ['undefined', 'function', 'symbol']
  const getType = (s) => {
    return Object.prototype.toString.call(s).replace(/\[object (.*?)\]/, '$1').toLowerCase()
  }

  // 非对象
  if (type !== 'object' || data === null) {
    let result = data
    // 特性四:
    // NaN 和 Infinity 格式的数值及 null 都会被当做 null。
    if ([NaN, Infinity, null].includes(data)) {
      result = 'null'
      // 特性一:
      // `undefined`、`任意的函数`以及`symbol值`被`单独转换`时,会返回 undefined
    } else if (commonKeys1.includes(type)) {
      // 直接得到undefined,并不是一个字符串'undefined'
      return undefined
    } else if (type === 'string') {
      result = '"'   data   '"'
    }

    return String(result)
  } else if (type === 'object') {
    // 特性五:
    // 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化
    // 特性六:
    // Date 日期调用了 toJSON() 将其转换为了 string 字符串(同Date.toISOString()),因此会被当做字符串处理。
    if (typeof data.toJSON === 'function') {
      return jsonstringify(data.toJSON())
    } else if (Array.isArray(data)) {
      let result = data.map((it) => {
        // 特性一:
        // `undefined`、`任意的函数`以及`symbol值`出现在`数组`中时会被转换成 `null`
        return commonKeys1.includes(typeof it) ? 'null' : jsonstringify(it)
      })

      return `[${result}]`.replace(/'/g, '"')
    } else {
      // 特性二:
      // 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
      if (['boolean', 'number'].includes(getType(data))) {
        return String(data)
      } else if (getType(data) === 'string') {
        return '"'   data   '"'
      } else {
        let result = []
        // 特性八
        // 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性
        Object.keys(data).forEach((key) => {
          // 特性三:
          // 所有以symbol为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
          if (typeof key !== 'symbol') {
            const value = data[key]
            // 特性一
            // `undefined`、`任意的函数`以及`symbol值`,出现在`非数组对象`的属性值中时在序列化过程中会被忽略
            if (!commonKeys1.includes(typeof value)) {
              result.push(`"${key}":${jsonstringify(value)}`)
            }
          }
        })

        return `{${result}}`.replace(/'/, '"')
      }
    }
  }
}

测试一把:

// 1. 测试一下基本输出
console.log(jsonstringify(undefined)) // undefined 
console.log(jsonstringify(() => { })) // undefined
console.log(jsonstringify(Symbol('前端胖头鱼'))) // undefined
console.log(jsonstringify((NaN))) // null
console.log(jsonstringify((Infinity))) // null
console.log(jsonstringify((null))) // null
console.log(jsonstringify({
  name: '前端胖头鱼',
  toJSON() {
    return {
      name: '前端胖头鱼2',
      sex: 'boy'
    }
  }
}))
// {"name":"前端胖头鱼2","sex":"boy"}

// 2. 和原生的JSON.stringify转换进行比较
console.log(jsonstringify(null) === JSON.stringify(null));
// true
console.log(jsonstringify(undefined) === JSON.stringify(undefined));
// true
console.log(jsonstringify(false) === JSON.stringify(false));
// true
console.log(jsonstringify(NaN) === JSON.stringify(NaN));
// true
console.log(jsonstringify(Infinity) === JSON.stringify(Infinity));
// true
let str = "前端胖头鱼";
console.log(jsonstringify(str) === JSON.stringify(str));
// true
let reg = new RegExp("\w");
console.log(jsonstringify(reg) === JSON.stringify(reg));
// true
let date = new Date();
console.log(jsonstringify(date) === JSON.stringify(date));
// true
let sym = Symbol('前端胖头鱼');
console.log(jsonstringify(sym) === JSON.stringify(sym));
// true
let array = [1, 2, 3];
console.log(jsonstringify(array) === JSON.stringify(array));
// true
let obj = {
  name: '前端胖头鱼',
  age: 18,
  attr: ['coding', 123],
  date: new Date(),
  uni: Symbol(2),
  sayHi: function () {
    console.log("hello world")
  },
  info: {
    age: 16,
    intro: {
      money: undefined,
      job: null
    }
  },
  pakingObj: {
    boolean: new Boolean(false),
    string: new String('前端胖头鱼'),
    number: new Number(1),
  }
}
console.log(jsonstringify(obj) === JSON.stringify(obj)) 
// true
console.log((jsonstringify(obj)))
// {"name":"前端胖头鱼","age":18,"attr":["coding",123],"date":"2021-10-06T14:59:58.306Z","info":{"age":16,"intro":{"job":null}},"pakingObj":{"boolean":false,"string":"前端胖头鱼","number":1}}
console.log(JSON.stringify(obj))
// {"name":"前端胖头鱼","age":18,"attr":["coding",123],"date":"2021-10-06T14:59:58.306Z","info":{"age":16,"intro":{"job":null}},"pakingObj":{"boolean":false,"string":"前端胖头鱼","number":1}}

// 3. 测试可遍历对象
let enumerableObj = {}

Object.defineProperties(enumerableObj, {
  name: {
    value: '前端胖头鱼',
    enumerable: true
  },
  sex: {
    value: 'boy',
    enumerable: false
  },
})

console.log(jsonstringify(enumerableObj))
// {"name":"前端胖头鱼"}

// 4. 测试循环引用和Bigint

let obj1 = { a: 'aa' }
let obj2 = { name: '前端胖头鱼', a: obj1, b: obj1 }
obj2.obj = obj2

console.log(jsonstringify(obj2))
// TypeError: Converting circular structure to JSON
console.log(jsonStringify(BigInt(1)))
// TypeError: Do not know how to serialize a BigInt

通过上面测试可以看出,jsonstringify基本和JSON.stringify表现一致,

到此这篇关于学习JSON.stringify的9大特性和转换规则的文章就介绍到这了,更多相关JSON.stringify内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

学习JSON.stringify的9大特性和转换规则的更多相关文章

  1. JSON.stringify的多种用法总结

    这篇文章主要给大家介绍了关于JSON.stringify使用的相关资料, JSON.stringify()方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,需要的朋友可以参考下

  2. 学习JSON.stringify的9大特性和转换规则

    本文介绍JSON.stringify9大特性和转换规则,JSON.stringify()方法将一个JavaScript对象或值转换为 JSON 字符串,如果指定了一个replacer 函数,则可以选择性地替换值,或者指定的replacer是数组,则可选择性地仅包含数组指定的属性,更多内容需要的小火煸可以参考下面温行内容

  3. Ajax的特性及乱码问题

    ajax的全称是asynchronous javascript and XML ,它是异步的js和XML。它是局部刷新,异步操作。这篇文章给大家介绍了ajax的特性及乱码问题,感兴趣的朋友一起看看吧

  4. JS中构造函数的基本特性与优缺点

    这篇文章介绍了JS中构造函数的基本特性与优缺点,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 前端使用JSON.stringify实现深拷贝的巨坑详解

    这篇文章主要为大家介绍了JSON.stringify实现深拷贝的巨坑详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  6. redisson特性及优雅实现示例

    这篇文章主要为大家介绍了redisson特性及优雅实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  7. PHP 5昨天隆重推出--PHP 5/Zend Engine 2.0新特性

    前言今天突然想到PHP官方网站上一转,一眼就看到PHP5推出的通告。虽然以前看到过PHP5的预告,但还是仔细看了PHP5/ZendEngine2.0新特性一文,一股JAVA气息扑面而来...特将该文试译出来,首发于CSDN网站,以飨读者。PHP5/ZendEngine2.0新特性徐唤春译sfwebsite@hotmail.comhttp://www.php.net/zend-engine-2.php全新的对象模型PHP中的对象处理部分已完全重写,具有更佳的性能和更多的功能。>私有和保护方法在PHP5中,还

  8. php5.6.x到php7.0.x特性小结

    这篇文章主要介绍了php5.6.x到php7.0.x特性,总结分析了php5.6.x到php7.0.x各种常用特性及新增特性,以及相关使用技巧,需要的朋友可以参考下

  9. PHP5的XML新特性

    面向的读者这篇文章的面向对象是所有对PHP5的XML新功能感兴趣的各个水平的PHP开发者。因此相对于PHP4,PHP5对于XML的支持更受到了重视。这篇文章将对PHP5中关于XML的所有令人激动的新特性逐一介绍。基于所有这些原因,PHP的XML开发者决定在PHP5重写全部代码,并遵循使用标准。同时还加入了PHP独有的SimpleXML扩展和符合标准的SOAP扩展。在PHP4可以使用DOMXML来对此进行操作,DOMXML的最主要问题是它不符合标准的命名方法。它与PHP5的DOM部分基本上相同。

  10. JSON.stringify实例详解以及灵活运用

    在向服务器发送数据时一般是字符串,我们可以使用 JSON.stringify()方法将JavaScript对象转换为字符串,下面这篇文章主要给大家介绍了关于JSON.stringify及灵活运用的相关资料,需要的朋友可以参考下

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部