前言

在以前的web开发中,我们多数选择纯文本XML作为我们的提交的数据格式,大多数是XML,少数纯文本。其实从AJAX(Asynchronous JavaScript and XML)的命名我们也知道,数据交换是大多数通过XML格式进行的。但是XML格式有一个缺点,就是文档构造复杂,需要传输比较多的字节数,并且解析起来也比较麻烦。所以就创建了JSON这种数据描述格式,可以很简单的就描述很复杂的数据。同时独立于语言,这样就可以在多种语言内使用。也正是因为这个,JSON的轻便性逐渐得到重视,后来替代XML成为最主要的数据传输格式。

ps: 当然,虽然说了很多JSON的好处,你就以为不用学习XML了吗?肯定不是啦,微信公共平台接口中的有是有相当一部分接口使用了XML的。

JSON对象包含两个方法: 用于解析 JavaScript Object Notation (JSON) 的 parse() 方法,以及将对象/值转换为 JSON 字符串的 stringify() 方法。除了这两个方法, JSON 这个对象本身并没有其他作用,也不能被调用或者作为构造函数调用。

ps: JSON:并不是 JavaScript 的子集。尽管不是严格意义上的子集,JSON 非常接近 JavaScript 语法的子集。

JavaScript 与 JSON 的区别

JavaScript类型 JSON 的不同点
对象和数组 属性名称必须是双引号括起来的字符串;最后一个属性后不能有逗号。
数值 禁止出现前导零(JSON.stringify 方法自动忽略前导零,而在 JSON.parse 方法中将会抛出 SyntaxError);如果有小数点, 则后面至少跟着一位数字。

针对于这两点,我们举个例子,对于对象和数组类型:

  1. // 属性名称必须是双引号括起来的字符串
  2. const test1 = "['1','1']"
  3. JSON.parse(test1) // Uncaught SyntaxError: Unexpected token ' in JSON at position 1
  4. const test2 = "{'name':'gating'}"
  5. JSON.parse(test2) // Uncaught SyntaxError: Unexpected token o in JSON at position 1
  6. const test3 = '["2","1"]'
  7. JSON.parse(test3) // ["2","1"]
  8. const test4 = '{"name":"gating"}'
  9. JSON.parse(test3) // {name: "gating"}
  10. // 最后一个属性后不能有逗号
  11. const test5 = '["2","1",]'
  12. JSON.parse(test5) // Uncaught SyntaxError: Unexpected token ] in JSON at position 9
  13. const test6 = '{"name":"gating",}'
  14. JSON.parse(test6) // Uncaught SyntaxError: Unexpected token } in JSON at position 17

对于数值类型:

  1. // 禁止出现前导零
  2. const test7 = [00010, 1]
  3. JSON.stringify(test7) // "[8,1]"
  4. const test8 = "[00010, 1]"
  5. JSON.parse(test8) // SyntaxError: Unexpected number in JSON at position 2**
  6. // 如果有小数点,则后面至少跟着一位数字。
  7. const test9 = [1, 1.]
  8. JSON.stringify(test9) // "[1,1]"
  9. const test10 = "[1, 1.]"
  10. JSON.parse(test10) // Uncaught SyntaxError: Unexpected token ] in JSON at position 6

ps: 0+数字表示8进制,- -所以上面打印出来的是8,同样的有:0b+数字(二进制)0x+数字(16进制),其实0o+数字也可以表示8进制

其实还有一个字符串类型,JavaScriptJSON处理是不一致的,这里为啥我只写两点呢?是因为还有现在在新版的Chrome已经可以解析正常,但是想具体可以查看下面参考链接自行了解,这里就不展开了

参考:JavaScript 与 JSON 的区别,字符串那一点我在Chrome 85.0.4183.83以及可以正常解析

stringify

stringify() 方法用于将JavaScript值转换为JSON字符串。

虽然我们经常用这个方法,但我想估计有不少人不知道它居然有三个参数之多吧?分别是valuereplacerspace

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

  • value

    • 将要序列化成 一个 JSON 字符串的值。
  • replacer 可选
    • 如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。
  • space 可选
    • 指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格;如果该参数为字符串(当字符串长度超过10个字母,取其前10个字母),该字符串将被作为空格;如果该参数没有提供(或者为 null),将没有空格。

value参数

通常我们最常用的就是第一个参数,也就是value参数,但是你知道它的运算规则是怎么样的吗?闲话不多说,直接上例子

NaN 和 Infinity 格式的数值及 null 都会被当做 null

  1. JSON.stringify([NaN, Infinity, null]) // [null,null,null]

undefined、任意的函数以及 symbol 值的处理

出现在非数组对象的属性值中时,在序列化过程中会被忽略

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

出现在数组中时被转换成null

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

单独出现则会返回undefined

  1. JSON.stringify(undefined) // undefined
  2. JSON.stringify(Object) // undefined
  3. JSON.stringify(Symbol("")) // undefined

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

  1. JSON.stringify({[Symbol("foo")]: "foo"}) // "{}"
  2. JSON.stringify({[Symbol.for("foo")]: "foo"}, [Symbol.for("foo")]) // "{}"

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

  1. JSON.stringify({
  2. toJSON: function() {
  3. return 'gating'
  4. }
  5. }) // "gating"
  6. const name = {
  7. age: 18,
  8. toJSON: function () {
  9. return 'gating'
  10. },
  11. }
  12. JSON.stringify({ name }) // {"name":"gating"}

又因为Date对象上挂载了一个toJSON方法,所以针对于Date类型,它默认就会调用Date类型上的toJSON方法(同Date.toISOString())

  1. JSON.stringify(new Date("2020/01/01")) // "2019-12-31T16:00:00.000Z" 因为中国在东八区,所以相差了8小时

ps: 东八区(UTC/GMT+08:00)是比世界协调时间(UTC)/格林尼治时间(GMT)快8小时的时区

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

  1. JSON.stringify(
  2. Object.create(null, {
  3. x: { value: 'x'}, // enumerable 默认为不可枚举
  4. y: { value: 'y', enumerable: true },
  5. })
  6. ) // "{"y":"y"}"
  7. const map = new Map()
  8. Object.defineProperty(map, 'name', {
  9. value: 'gating',
  10. enumerable: true
  11. })
  12. Object.defineProperty(map, 'age', {
  13. value: 18
  14. })
  15. JSON.stringify(map) // {"name":"gating"}
  16. JSON.stringify(new Set) // {}
  17. JSON.stringify(new RegExp) // {}

看到这里,你基本已经知道运算规则是怎么样滴了,也就知道了为什么JSON.stringify + JSON.parse不能转换函数、正则、Error等对象了吧?

那么接下来就要了解下更有意思的东西啦,就是stringify第二个参数replacer

replacer参数

replacer 参数可以用来来更改默认的字符串化的行为。它可以是一个函数或者一个数组。如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。

作为函数

在开始时, replacer 函数会被传入一个空字符串作为 key 值,代表着要被 stringify 的这个对象。随后每个对象或数组上的属性会被依次传入。

这句话看不太懂?不要紧,举个例子你就懂了

  1. function replacer(key, value) {
  2. console.log('[' + key + ']:' + value)
  3. return value
  4. }
  5. JSON.stringify({ name: 'gating', age: 18 }, replacer)
  6. // []:[object Object]
  7. // [name]:gating
  8. // [age]:18

上面例子中,他会执行三次,也就是一开始他会默认传一个空字符串作为键,而键值是整个对象;第二次键为name,键值为gating,以此类推。

当然,这里也有个特别需要注意的点,就是返回的是一个对象的时候,该对象递归地序列化成 JSON 字符串,对每个属性调用replacer方法。除非该对象是一个函数,这种情况将不会被序列化成 JSON 字符串。

比如:

  1. function replacer(key, value) {
  2. return {a:1}
  3. }
  4. JSON.stringify({}, replacer)

因为我们每次返回的都是对象,那么每次都会调用replacer,所以会造成堆栈溢出,那么我们举个不会溢出的小栗子例子吧

JS中有趣的内置对象-JSON的更多相关文章

  1. js中常用的内置对象

    Arguments 函数参数集合 arguments[ ] 函数参数的数组 Arguments 一个函数的参数和其他属性 Arguments.callee 当前正在运行的函数     Argument ...

  2. js中常见的内置对象

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  3. JS中的日期内置函数

    用JS中的日期内置函数实现在页面显示:“今天是:2013年9月26日14:32:45”. var date=new Date(Date.parse('9/26/2013 14:32:45'));   ...

  4. 在IDEA中使用JSP中的out内置对象,out.println()——println红色解决方法

    今天在学习JSP的时候,在jsp中使用out内置对象,开发工具用的是IDEA,结果如下图所示 郁闷了半天找度娘,可能关键字输的不准确,乱七八糟的方法一大堆,什么加依赖啊啥的,反正都不管用,最后找到一篇 ...

  5. js学习---常用的内置对象(API)小结 :

    内置对象(API): 日期 Date: getFullYear() 返回完整的4位的年份  如:2016 getMonth()    返回月份,从0开始 getDate()   返回当前月的第几天,当 ...

  6. 在jsp中常用的内置对象(5个)小总结和两种页面跳转方式(服务器端调转、客户端跳转)的区别

    jsp中常用的几个内置对象: 一.request对象 主要作用:  (1)获取请求页面的信息   比如:request.getParameter("参数名");  (2)获取客户端 ...

  7. Servlet中获取JSP内置对象

    方便自己查询,嫌低级的勿喷.... 1.request 在servlet的doGet和doPost的参数中就有HttpServletRequest req参数,而JSP内置request对象就是Htt ...

  8. Servlet中的jsp内置对象

    Servlet和jsp本质相同,那么为什么还要使用jsp呢,原来的servlet又有什么不好的呢. Servlet和jsp可以做完全相同的事情,就要借助jsp的内置对象们,比如request,resp ...

  9. jsp中9个内置对象与servlet对应关系及四个作用域

    参考:  <jsp&servlet学习笔记.第2版.林信良><JSR-245 JavaServer Pages 2.2 Maintenance Release Specifi ...

随机推荐

  1. Springboot2.x整合logback slf4j

    Springboot项目的pom里引入的parent <parent> <groupId>org.springframework.boot</groupId> &l ...

  2. 在龙芯mips64el平台编译bmon

    bmon 是一个 实时命令行流量监控软件,但作者在github并没有提供mips64el的版本.下面记录一下编译过程.可以在这里下载bmon.v4.0.linux-mips64el.tar.gz. 环 ...

  3. 2020-06-21:ZK的哪些基础能力构建出了哪些上层应用?

    福哥答案2020-06-21: 福哥口诀法:数负命Ma集配分(使用场景:数据发布订阅.负载均衡.命名服务.Master 选举.集群管理.配置管理.分布式队列和分布式锁) 数据发布订阅:dubbo的rp ...

  4. windows 下 安装 RabbitMQ

    RabbitMQ是一个在AMQP协议标准基础上完整的.可复用的企业消息系统.它遵循Mozilla Public License开源协议,采用 Erlang 实现的工业级的消息队列(MQ)服务器.它扮演 ...

  5. Android CC框架中,新建组件无法显示布局问题

    出错: 当在创建新的组件时,跳转到新组件成功,但是无法正确显示布局,即获取到布局文件的控件等. 原因: 当在创建新的组件时,默认生成MainActivity以及其布局activity_main.每个组 ...

  6. centos7上借助于xargs快速查询并卸载rpm软件

    在centos上卸载某些软件的时候,如果查询的软件包比较多,可以考虑使用xargs,边查询边卸载 如:下面在查询mysql包时候,将查询结果通过管道传送给xargs,然后使用rpm -e --node ...

  7. extJS--尚

    ExtJS依赖JavaScript,JavaScript推荐两本书:<JavaScript高级程序设计>初阶阶段, <JavaScript设计模式>中级阶段 fun1();// ...

  8. 构造 IPv6 报文

    #!/usr/bin/python from scapy.all import * a=IPv6(nh=58, src='fe80::214:f2ff:fe07:af0', dst='ff02::1' ...

  9. 使用 gopacket 进行数据包捕获,注入和分析

    原文链接:https://www.devdungeon.com/content/packet-capture-injection-and-analysis-gopacket 接口文档:https:// ...

  10. Learning in Spiking Neural Networks by Reinforcement of Stochastic Synaptic Transmission

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Summary 众所周知,化学突触传递是不可靠的过程,但是这种不可靠的函数仍然不清楚.在这里,我考虑这样一个假设,即大脑利用突触传递的随机 ...