继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

「JSON」并不是JS对象

MMTTMM
关注TA
已关注
手记 425
粉丝 65
获赞 364

webp

$ 什么是JSON ?

  JSON: JS 对象表示法,它是一种数据格式,不是一种编程语言。JSON不支持变量,函数或对象实例,他就是一种表示结构化数据的格式,虽与JS中表示数据的某些语法相同,但它并不局限于JS范畴,JSON并不从属于JS,很多语言都有针对JSON的解析器和序列化器。

$ 用JSON 表示值:

JSON的语法可以表示以下三种类型的值

  1. 简单值: 字符串,数值,布尔值,null

  2. 对象: 对象是一种复杂的数据类型,表示一组无序的键值对,键和值可以是简单值

  3. 数组: 数组也是一种复杂的数据类型,表示一组有序的值的列表

@ JOSN表示简单值

  JSON更多的表示复杂的数据额类型,而简单值只是整个数据结构中的一部分

5   // JSON表示数值5的方式"Hello world"  // JSON表示字符串的方式

码农注释】JS字符串与JSON表示字符串最大区别在于,后者必须使用双引号,单引号会导致语法错误。

@ JSON表示对象

  JSON表示对象是我们最常用的JSON数据格式之一,JSON中的对象与JS中的对象还是有些不同。

  1. 没有声明变量(JSON中就没有变量的概念)

  2. 末尾没有分号(JS对象不是JS语句,只是数据表示法,不需要分号)

  3. 对象的属性必须加双引号   (!真是个常见的错误)

// JS 字面量对象var person = {  name: "Nicholas",  age: 29};// JSON表示对象的方式{  "name": "Nicholas",  "age": 29}

码农注释】JSON对象键值对本质上是用JSON简单值表示的,简单值规则中就需要加双引号,因此这里也要加。当然JSON对象属性值也可以用JSON对象表示,就是多层嵌套而已,道理相同。

@ JSON表示数组

  JSON数组的数据表示这更加灵活,它与JS数组的区别和JSON对象上的区别类似不赘述,值得注意的是数组中有对象数组这种数据结构,即[{}, {}, {}]形式

// JS数组var values = [25, "Hi", true];// JSON数组[25, "Hi", true]// 更复杂一些的JSON数组[
  {    "title": "JavaScriot",    "author": ["Nicholas"],    "edition": 3
  },
  {    "title": "Ajax",    "author": ["Nicholas", "Joe", "Jeremy"],    "edition": 2
  },
]

$ JSON对象 解析与序列化

  JSON流行的原因,并不止因为它类似JS的语法,更重要的是因为可以把JSON数据结构解析为有用的JS对象。早期的JSON解析器基本上就是使用JS的eval()函数。在ES5的规范中为避免eval()执行一些恶意代码,改用使用shim.

  JSON对象有两个方法:stringify()用于将JS对象序列化为JSON字符串,parse()用于把JSON字符串解析为原生JS值

(1)序列化:JSON.stringify()方法

JSON.stringify()方法是将一个JS值(对象或者数组)序列化为一个 JSON字符串,值得注意的是,序列化JS对象时,所有函数及原型成员都会被有意忽略,所有值为undefined的也将被忽略,最终都是值为有效的JSON数据类型的实例属性。

var book = {  title: 'JavaScript',  authors: ["Nichoas C. Zakas"],  edition: 3,  year: 2011,  master: undefined,  lastRead: function() {    return "20180812"
  }
};var jsonBook = JSON.stringify(book);console.log(jsonBook);  // 见 码农注释console.log(typeof(jsonBook));  // string - 这里指JSON字符串

码农注释JSON.stringify()方法将名为book的JS对象序列化成了JSON字符串,并将该字符串存放在变量jsonBook上。jsonBook打印结果为{"title":"JavaScript","authors":["Nichoas C. Zakas"],"edition":3,"year":2011} 可以看到键值对都加了双引号。注意jsonBook中并没有属性lastReadmaster的序列化值,因为它们被故意忽略了。

@ JSON.stringify()序列化选项(参数)

stringify()函数第一个参数是JS对象我们已经清楚,它还能接受另外两个参数,给要序列化的JS对象增加序列化方式。第一个参数是过滤器,第二个参数是 字符串缩进。

1. 增加过滤器(替换:replacer)
可增加两种过滤器
(1)数组形式,包含想序列化的属性名。未出现的直接过滤。
(2)过滤函数。根据函数规则确定结果,未出现的不过滤,显示原值。

// 增加数组过滤器。数字的值为需要序列化的属性名var book = {  title: 'JavaScript',  authors: ["Nichoas C. Zakas"],  edition: 3,  year: 2011};var jsonBook = JSON.stringify(book, ['edition','title']);console.log(jsonBook)  // {"edition":3,"title":"JavaScript"}

执行结果: {"edition":3,"title":"JavaScript"}
码农解释】序列化结果的属性顺序与数组过滤器的顺序相同,该例中titleedition后出现

// 过滤器为函数var book = {  title: 'JavaScript',  authors: ["Nichoas C. Zakas"],  edition: 3,  year: 2011,  nothing: undefined,  lastRead: function(){    return "20180814"
  }
};// 函数不能取到外边定义var jsonBook = JSON.stringify(book, function(key, value){  switch (key) {    case "authors": 
      return value.join(",");    case "year":      return 2018; 
    default:      return value;
    }
});console.log(jsonBook) 
// {"title":"JavaScript","authors":"Nichoas C. Zakas","edition":3,"year":2018}

码农解释】函数过滤器一定要提供default项,以便其他值都能正常出现在结果中,(其实不加也能保留其他的属性,但可能存在兼容问题)。实际上,第一次调用这个函数过滤器,传入的键是一个空字符串,而值就是book对象。

2.增加字符串缩进
控制结果中的缩进,将堆放的字符串数据改用具有格式的字符串格式显示。缩进办法有两种:
(1)数值,表示缩进空格数。
(2)字符串,用字符串填充缩进。

var book = {  title: 'JavaScript',  authors: ["Nichoas C. Zakas"],  edition: 3,  year: 2011,  nothing: undefined,  lastRead: function(){    return "20180814"
  }
};var jsonBook = JSON.stringify(book, null, 4);console.log(jsonBook)

】执行结果为字符串,只不过因为缩进显示成JSON对象如下(如果第三个参数是字符串,则缩进的填充字符为字符串)

{    "title": "JavaScript",    "authors": [        "Nichoas C. Zakas"
    ],    "edition": 3,    "year": 2011}

3. toJSON(): 序列化的另一个办法
  有时候,JSON.stringify()还是不能满足对某些对象进行自定义序列化的需求,这种情况下我们可以给对象定义toJSON()方法,他表示在执行JSON.stringify时的执行结果。原生Date也有个toJSON()方法,将在下一部分说明。

var book = {  title: 'JavaScript',  authors: ["Nichoas C. Zakas"],  edition: 3,  year: 2011,  nothing: undefined,  toJSON: function() {    return this.title
  }
}; 
var jsonBook = JSON.stringify(book);console.log(jsonBook);  // "JavaScript"
关于不同序列化办法执行的优先级

(1)如果存在toJSON()方法,而且能通过他获取有效的值,这调用该方法,否则返回对象本身
(2)如果提供了第二个参数,应用这个函数过滤器,传入函数过滤器的值是第(1)步返回的值
(3)对第(2)步的值进行相应的序列化
(4)如果提供了第三个参数,执行相应的格式化

var book = {  title: 'JavaScript',  authors: ["Nichoas C. Zakas"],  edition: 3,  year: 2011,  nothing: undefined,  toJSON: function(){    return this.title;
  }
}; 
var jsonBook = JSON.stringify(book, function(key, value) {  switch (key) {    case "title":      return 'Ajax';    default:      return value;
  }
}, 4);console.log(jsonBook);  
// "JavaScript" 因为优先执行了toJSON(),没有执行第二个参数

(2)解析: JSON.parse() 方法

JSON.parse()是将JSON字符串解析为原生JS值的函数,在上述代码前提下,继续执行以下代码

var bookCopy = JSON.parse(jsonBook);

最终得到的是以下结果

var bookCopy = {  title: 'JavaScript',  authors: ["Nichoas C. Zakas"],  edition: 3,  year: 2011}

码农解释】为什么数据发生了变化?虽然JSON.parse()JSON.stringify()在大多数情况下都是相反的过程,但当原始数据存在JSON.stringify()主动忽略的属性时,得到的JSON字符串中已经不存在这部分属性,因此执行parse()得到的原生JS与原数据不同。

注意对比以下情形

var bookCopy = book;

  此时得到的bookCopy的结果与原数据一致。相信很多人能明白这一点,因为JS对象是应用类型,这种方式赋值只是复制了一份原数据的引用到bookCopy上,也就是说,修改book数据的同时也会影响到bookCopy上的值。
  但由JSON.stringify()序列化和JSON.parse()解析后,得到的bookCopybook是没有任何关系的两个变量,他们的值有可能相同,有可能不同,这取决于原数据是否含有主动忽略的属性。

JOSN.parse()不允许用逗号作为结尾

// both throw a SyntaxErrorJSON.parse("[1, 2, 3, 4, ]"); 
JSON.parse('{"foo" : 1, }');


1. 解析选项:
JSON.parse()也可以接受另一个参数,该参数是个函数,将在每个键值对上调用,为区别序列化的第二个参数,该函数被称为还原函数(reviver)。但实际上他们的签名是相同的,接受两个参数,keyvalue,而且都需要返回一个值。

  需要注意的是,如果还原函数返回undefined,则表示要从结果中删除相应的键,如果返回其他值,则将该值插入到结果中。

$ JSON的几个使用场景

  JSON格式的数据在一些场景的使用中,如果直接传输会出现异常,此时就需要我们先对数据做一次序列化,否则很可能在访问的时候出现[object,object]

(1) JSON 在 localStorage中的使用

  一些时候,你想存储用户创建的一个对象,并且,即使在浏览器被关闭后仍能恢复该对象。

var session = {    'screens' : [],    'state' : true};
session.screens.push({"name":"screenA", "width":450, "height":250});
session.screens.push({"name":"screenB", "width":650, "height":350});
session.screens.push({"name":"screenC", "width":750, "height":120});// setItem前要先使用 JSON.stringify 转换为 JSON 字符串localStorage.setItem('session', JSON.stringify(session));// 然后通过JSON.parse()取数据var restoredSession = JSON.parse(localStorage.getItem('session'));

(2) Date.prototype.toJSON()

toJSON() 方法返回Date对象的字符串形式。

var event = new Date('August 19, 1975 23:15:30 UTC');var jsonDate = event.toJSON();console.log(jsonDate);// expected output: 1975-08-19T23:15:30.000Zconsole.log(new Date(jsonDate).toUTCString());// expected output: Tue, 19 Aug 1975 23:15:30 GMT

$ JSON的深拷贝【难点】

  先明白两个概念: 什么是深拷贝(深复制),什么是浅拷贝(浅复制)?对于值类型,深拷贝与浅拷贝没什么区别,关键是对于引用类型的数据结构

   浅复制(Shallow Copy) —- 对于引用类型,只复制了指向堆内存中的地址引用,并没有开辟新的栈空间。对于浅复制的两个变量,任何一个的值发生了改变,另一个也会随着改变。

深复制(Deep Copy) —- 开辟了一个新的栈空间用来创建一个原始字段的内容相同的对象。拷贝前后的两个变量完全相互独立,修改其中一个对象的属性值不会影响另一个对象的属性值。

@ 深拷贝的实现办法

1. 通过递归问解析解决 (推荐)
//深复制,要想达到深复制就需要用递归function deepCopy(o,c){  var c = c || {}  for (var i in o) {    if (typeof o[i] === 'object') {      //要考虑深复制问题了
      if (o[i].constructor === Array) {        //这是数组
        c[i] =[]
      } else {        //这是对象
        c[i] = {}
      }
      deepCopy(o[i],c[i])
    } else {
      c[i] = o[i]
    }
  }  return c
}// 测试代码var result = {name:'result'}var test= {  nation : '中国',  skincolr :'yellow',  deep: {    childDeep: {      names: ['judy', 'cici']
    }
  },  func: function() {    return "func return";
  }
}
result = deepCopy(test, result)console.dir(result)
  1. 通过JSON的解析解决 (只能使用在不存在被主动忽略属性的对象中)

var test= {  nation : '中国',  skincolr :'yellow',  deep: {    childDeep: {      names: ['judy', 'cici']
    }
  }
}var result = JSON.parse(JSON.stringify(test));console.log(result);



作者:果汁凉茶丶
链接:https://www.jianshu.com/p/f2fa75c3fd1b

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP