JSON:假如你情愿一层一层剥开我的心,你会发明...这里水很深——深切明白JSON

我们先来看一个JS中罕见的JS对象序列化成JSON字符串的题目,叨教,以下JS对象经由历程JSON.stringify后的字符串是如何的?先不要急着复制粘贴到控制台,先自身翻开一个代码编辑器或许纸,写写看,写完再去细致对照你的控制台输出,假若有误记得看完全文并批评,哈哈。

var friend={
    firstName: 'Good',
    'lastName': 'Man',
    'address': undefined,
    'phone': ["1234567",undefined],
    'fullName': function(){
        return this.firstName + ' ' + this.lastName;
    }
};

JSON.stringify(friend);//这一行返回什么呢?

第二个题目,假如我想在终究JSON字符串将这个’friend’的姓名悉数变成大写字母,也就是把”Good”变成”GOOD”,把”Man”变成”MAN”,那末能够怎样做?

基于以上两个题目,我们再追根究底问一下,JSON究竟是什么东西?为何JSON就是易于数据交流?JSON和JS对象的区分?JS中JSON.parseJSON.stringify和不罕见的toJSON,这几个函数的参数和处置惩罚细节究竟是如何的?

迎接进入本次“深挖JSON之旅”,下文将从以下几个方面去邃晓JSON:

  • 起首是对“JSON是一种轻量的数据交流花样”的邃晓;

  • 然厥后看经常被等量齐观的JSON和JS对象的区分;

  • 末了我们再来看JS中这几个JSON相干函数详细的实行细节。

愿望全文能让如之前的我一样对JSON一孔之见的亲能说清晰JSON是什么,也能闇练运用JSON,不看控制台就晓得JS对象序列化成JSON字符串后输出是啥。

一、JSON是一种花样,基于文本,优于轻量,用于交流数据

假如没有去过JSON的官方引见能够去一下这里,官方引见第一、二段已很清晰地表述了JSON是什么,我将JSON是什么提炼成以下几个方面:

1. 一种数据花样

什么是花样?就是范例你的数据要怎样示意,举个栗子,有个人叫“二百六”,身高“160cm”,体重“60kg”,如今你要将这个人的这些信息传给他人或许别的什么东西,你有许多种挑选:

  • 姓名“二百六”,身高“160cm”,体重“60kg”

  • name="二百六"&height="160cm"&weight="60kg"

  • <person><name>二百六</name><height>160</height><weight>60</weight></person>

  • {"name":"二百六","height":160,"weight":60}

  • … …

以上一切挑选,通报的数据是一样的,然则你能够看到情势是能够形形色色的,这就是种种差异花样化后的数据,JSON是个中一种示意体式格局。

2. 基于文本的数据花样

JSON是基于文本的数据花样,相关于基于二进制的数据,所以JSON在通报的时刻是通报相符JSON这类花样(至于JSON的花样是什么我们第二部份再说)的字符串,我们常会称为“JSON字符串”。

3. 轻量级的数据花样

在JSON之前,有一个数据花样叫xml,如今照样普遍在用,然则JSON越发轻量,如xml须要用到许多标签,像上面的例子中,你能够显著看到xml花样的数据中标签自身占有了许多空间,而JSON比较轻量,即雷同数据,以JSON的花样占有的带宽更小,这在有大批数据请乞降通报的状况下是有显著上风的。

4. 被普遍地用于数据交流

轻量已是一个用于数据交流的上风了,但更重要的JSON是易于浏览、编写和机械剖析的,即这个JSON对人和机械都是友爱的,而且又轻,独立于言语(因为是基于文本的),所以JSON被普遍用于数据交流。

以前端JS举行ajax的POST请求为例,后端PHP处置惩罚请求为例:

  1. 前端组织一个JS对象,用于包装要通报的数据,然后将JS对象转化为JSON字符串,再发送请求到后端;

  2. 后端PHP接收到这个JSON字符串,将JSON字符串转化为PHP对象,然后处置惩罚请求。

能够看到,雷同的数据在这里有3种差异的表现情势,离别是前端的JS对象、传输的JSON字符串、后端的PHP对象,JS对象和PHP对象显著不是一个东西,然则因为人人用的都是JSON来通报数据,人人都能邃晓这类数据花样,都能把JSON这类数据花样很轻易地转化为自身能邃晓的数据结构,这就轻易啦,在其他种种言语环境中交流数据都是云云。

二、JSON和JS对象之间的“八卦”

许多时刻都听到“JSON是JS的一个子集”这句话,而且这句话我曾也一向这么以为,每一个相符JSON花样的字符串你剖析成js都是能够的,直到厥后发明了一个奇奇怪怪的东西…

1. 两个实质差异的东西为何那末亲昵

JSON和JS对象实质上完全不是同一个东西,就像“斑马线”和“斑马”,“斑马线”基于“斑马”身上的条纹来显现和定名,然则斑马是活的,斑马线黑白生物。

一样,”JSON”全名”JavaScript Object Notation”,所以它的花样(语法)是基于JS的,但它就是一种花样,而JS对象是一个实例,是存在于内存的一个东西。

说句玩笑话,假如JSON是基于PHP的,能够就叫PON了,情势能够就是如许的了['propertyOne' => 'foo', 'propertyTwo' => 42,],假如如许,那末JSON能够如今是和PHP比较亲昵了。

别的,JSON是能够传输的,因为它是文本花样,然则JS对象是没要领传输的,在语法上,JSON也会越发严厉,然则JS对象就很松了。

那末两个差异的东西为何那末亲昵,因为JSON毕竟是从JS中演变出来的,语法邻近。

2. JSON花样别JS对象语法表现上严厉在哪

先就以“键值对为表现的对象”情势上,对照下二者的差异,至于JSON还能以如何的情势表现,对照完后再排列。

对照内容JSONJS对象
键名必需是加双引号可许可不加、加单引号、加双引号
属性值只能是数值(10进制)、字符串(双引号)、布尔值和null,
也能够是数组或许相符JSON请求的对象,
不能是函数、NaN, Infinity, -Infinity和undefined
爱啥啥
逗号题目末了一个属性背面不能有逗号能够
数值前导0不能用,小数点后必需有数字没限定

能够看到,相关于JS对象,JSON的花样更严厉,所以大部份写的JS对象是不相符JSON的花样的。

以下代码援用自这里

var obj1 = {}; // 这只是 JS 对象

// 可把这个称做:JSON 花样的 JavaScript 对象 
var obj2 = {"width":100,"height":200,"name":"rose"};

// 可把这个称做:JSON 花样的字符串
var str1 = '{"width":100,"height":200,"name":"rose"}';

// 这个可叫 JSON 花样的数组,是 JSON 的稍庞杂一点的情势
var arr = [
    {"width":100,"height":200,"name":"rose"},
    {"width":100,"height":200,"name":"rose"},
    {"width":100,"height":200,"name":"rose"},
];
        
// 这个可叫稍庞杂一点的 JSON 花样的字符串     
var str2='['+
    '{"width":100,"height":200,"name":"rose"},'+
    '{"width":100,"height":200,"name":"rose"},'+
    '{"width":100,"height":200,"name":"rose"},'+
']';

别的,除了罕见的“平常的”JSON花样,要么表现为一个对象情势{...},要么表现为一个数组情势[...],任何零丁的一个10进制数值、双引号字符串、布尔值和null都是有用相符JSON花样的。

这里有完全的JSON语法参考

3. 一个有意思的处所,JSON不是JS的子集

起首看下面的代码,你能够copy到控制台实行下:

var code = '"\u2028\u2029"';
JSON.parse(code); // works fine
eval(code); // fails

这两个字符\u2028\u2029离别示意行分隔符和段落分隔符,JSON.parse能够平常剖析,然则当作js剖析时会报错。

三、这几个JS中的JSON函数,弄啥嘞

在JS中我们重要会接触到两个和JSON相干的函数,离别用于JSON字符串和JS数据结构之间的转化,一个叫JSON.stringify,它很智慧,智慧到你写的不相符JSON花样的JS对象都能帮你处置惩罚成相符JSON花样的字符串,所以你得晓得它究竟干了什么,以免它只是自作智慧,然后让你Debug long time;另一个叫JSON.parse,用于转化json字符串到JS数据结构,它很严厉,你的JSON字符串假如组织地不对,是没要领剖析的。

而它们的参数不止一个,虽然我们经经常运用的时刻只传入一个参数。

别的,另有一个toJSON函数,我们较少看到,然则它会影响JSON.stringify

1. 将JS数据结构转化为JSON字符串 —— JSON.stringify

这个函数的函数署名是如许的:

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

下面将离别睁开带1~3个参数的用法,末了是它在序列化时做的一些“智慧”的事,要特别注重。

1.1 基础运用 —— 仅需一个参数

这个人人都邑运用,传入一个JSON花样的JS对象或许数组,JSON.stringify({"name":"Good Man","age":18})返回一个字符串"{"name":"Good Man","age":18}"

能够看到自身我们传入的这个JS对象就是相符JSON花样的,用的双引号,也没有JSON不接收的属性值,那末假如像开首谁人例子中的一样,how to play?不急,我们先举简朴的例子来申明这个函数的几个参数的意义,再来讲这个题目。

1.2 第二个参数能够是函数,也能够是一个数组

  • 假如第二个参数是一个函数,那末序列化历程当中的每一个属性都邑被这个函数转化和处置惩罚

  • 假如第二个参数是一个数组,那末只需包含在这个数组中的属性才会被序列化到终究的JSON字符串中

  • 假如第二个参数是null,那作用上和空着没啥区分,然则不想设置第二个参数,只是想设置第三个参数的时刻,就能够设置第二个参数为null

这第二个参数如果函数

var friend={
    "firstName": "Good",
    "lastName": "Man",
    "phone":"1234567",
    "age":18
};

var friendAfter=JSON.stringify(friend,function(key,value){
    if(key==="phone")
        return "(000)"+value;
    else if(typeof value === "number")
        return value + 10;
    else
        return value; //假如你把这个else分句删除,那末效果会是undefined
});

console.log(friendAfter);
//输出:{"firstName":"Good","lastName":"Man","phone":"(000)1234567","age":28}

假如制订了第二个参数是函数,那末这个函数必需对每一项都有返回,这个函数接收两个参数,一个键名,一个是属性值,函数必需针对每一个本来的属性值都要有新属性值的返回。

那末题目来了,假如传入的不是键值对的对象情势,而是方括号的数组情势呢?,比方上面的friend变成如许:friend=["Jack","Rose"],那末这个逐属性处置惩罚的函数接收到的key和value又是什么?假如是数组情势,那末key是索引,而value是这个数组项,你能够在控制台在这个函数内部打印出来这个key和value考证,记得要返回value,不然会失足。

这第二个参数如果数组

var friend={
    "firstName": "Good",
    "lastName": "Man",
    "phone":"1234567",
    "age":18
};

//注重下面的数组有一个值并非上面对象的任何一个属性名
var friendAfter=JSON.stringify(friend,["firstName","address","phone"]);

console.log(friendAfter);
//{"firstName":"Good","phone":"1234567"}
//指定的“address”因为没有在本来的对象中找到而被疏忽

假如第二个参数是一个数组,那末只需在数组中涌现的属性才会被序列化进效果字符串,只需在这个供应的数组中找不到的属性就不会被包含进去,而这个数组中存在然则源JS对象中不存在的属性会被疏忽,不会报错。

1.3 第三个参数用于美化输出 —— 不发起用

指定缩进用的空缺字符,能够取以下几个值:

  • 是1-10的某个数字,代表用几个空缺字符

  • 是字符串的话,就用该字符串替代空格,最多取这个字符串的前10个字符

  • 没有供应该参数 即是 设置成null 即是 设置一个小于1的数

var friend={
    "firstName": "Good",
    "lastName": "Man",
    "phone":{"home":"1234567","work":"7654321"}
};

//直接转化是如许的:
//{"firstName":"Good","lastName":"Man","phone":{"home":"1234567","work":"7654321"}}

var friendAfter=JSON.stringify(friend,null,4);
console.log(friendAfter);
/*
{
    "firstName": "Good",
    "lastName": "Man",
    "phone": {
        "home": "1234567",
        "work": "7654321"
    }
}
*/

var friendAfter=JSON.stringify(friend,null,"HAHAHAHA");
console.log(friendAfter);
/*
{
HAHAHAHA"firstName": "Good",
HAHAHAHA"lastName": "Man",
HAHAHAHA"phone": {
HAHAHAHAHAHAHAHA"home": "1234567",
HAHAHAHAHAHAHAHA"work": "7654321"
HAHAHAHA}
}
*/

var friendAfter=JSON.stringify(friend,null,"WhatAreYouDoingNow");
console.log(friendAfter);
/* 最多只取10个字符
{
WhatAreYou"firstName": "Good",
WhatAreYou"lastName": "Man",
WhatAreYou"phone": {
WhatAreYouWhatAreYou"home": "1234567",
WhatAreYouWhatAreYou"work": "7654321"
WhatAreYou}
}
*/

笑笑就好,别如许用,序列化是为了传输,传输就是能越小越好,加稀里糊涂的缩进符,剖析难题(假如是字符串的话),也弱化了轻量化这个特性。

1.4 注重这个函数的“小智慧”(重要)

假若有其他不确定的状况,那末最好的要领就是”Have a try”,控制台做下试验就清楚明了。

  • 键名不是双引号的(包含没有引号或许是单引号),会自动变成双引号;字符串是单引号的,会自动变成双引号

  • 末了一个属性背面有逗号的,会被自动去掉

  • 非数组对象的属性不能保证以特定的递次出如今序列化后的字符串中
    这个好邃晓,也就是对非数组对象在终究字符串中不保证属性递次和本来一致

  • 布尔值、数字、字符串的包装对象在序列化历程当中会自动转换成对应的原始值
    也就是你的什么new String("bala")会变成"bala"new Number(2017)会变成2017

  • undefined、恣意的函数(实在有个函数会发作奇异的事,背面会说)以及 symbol 值(symbol详见ES6对symbol的引见)

    • 出如今非数组对象的属性值中:在序列化历程当中会被疏忽

    • 出如今数组中时:被转换成 null

JSON.stringify({x: undefined, y: function(){return 1;}, z: Symbol("")});
//出如今非数组对象的属性值中被疏忽:"{}"
JSON.stringify([undefined, Object, Symbol("")]);
//出如今数组对象的属性值中,变成null:"[null,null,null]"
  • NaN、Infinity和-Infinity,不管在数组照样非数组的对象中,都被转化为null

  • 一切以 symbol 为属性键的属性都邑被完全疏忽掉,即使 replacer 参数中强迫指定包含了它们

  • 不可枚举的属性会被疏忽

2. 将JSON字符串剖析为JS数据结构 —— JSON.parse

这个函数的函数署名是如许的:

JSON.parse(text[, reviver])

假如第一个参数,即JSON字符串不是正当的字符串的话,那末这个函数会抛失足误,所以假如你在写一个后端返回JSON字符串的剧本,最好挪用言语自身的JSON字符串相干序列化函数,而假如是自身去拼接完成的序列化字符串,那末就特别要注重序列化后的字符串是不是是正当的,正当指这个JSON字符串完全相符JSON请求的严厉花样

值得注重的是这里有一个可选的第二个参数,这个参数必需是一个函数,这个函数作用在属性已被剖析然则还没返回前,将属性处置惩罚后再返回。

var friend={
    "firstName": "Good",
    "lastName": "Man",
    "phone":{"home":"1234567","work":["7654321","999000"]}
};

//我们先将其序列化
var friendAfter=JSON.stringify(friend);
//'{"firstName":"Good","lastName":"Man","phone":{"home":"1234567","work":["7654321","999000"]}}'

//再将其剖析出来,在第二个参数的函数中打印出key和value
JSON.parse(friendAfter,function(k,v){
    console.log(k);
    console.log(v);
    console.log("----");
});
/*
firstName
Good
----
lastName
Man
----
home
1234567
----
0
7654321
----
1
999000
----
work
[]
----
phone
Object
----

Object
----
*/

细致看一下这些输出,能够发明这个遍历是由内而外的,能够由内而外这个词人人会误会,最里层是内部数组里的两个值啊,然则输出是从第一个属性最先的,怎样就是由内而外的呢?

这个由内而外指的是关于复合属性来讲的,浅显地讲,遍历的时刻,从头至尾举行遍历,假如是简朴属性值(数值、字符串、布尔值和null),那末直接遍历完成,假如是碰到属性值是对象或许数组情势的,那末停息,先遍历这个子JSON,而遍历的准绳也是一样的,等这个复合属性遍历完成,那末再完成对这个属性的遍历返回。

实质上,这就是一个深度优先的遍历。

有两点须要注重:

  • 假如 reviver 返回 undefined,则当前属性会从所属对象中删除,假如返回了其他值,则返回的值会成为当前属性新的属性值。

  • 你能够注重到上面例子末了一组输出看上去没有key,实在这个key是一个空字符串,而末了的object是末了剖析完成对象,因为到了最上层,已没有真正的属性了。

3. 影响 JSON.stringify 的奇异函数 —— object.toJSON

假如你在一个JS对象上完成了toJSON要领,那末挪用JSON.stringify去序列化这个JS对象时,JSON.stringify会把这个对象的toJSON要领返回的值作为参数去举行序列化。

var info={
    "msg":"I Love You",
    "toJSON":function(){
        var replaceMsg=new Object();
        replaceMsg["msg"]="Go Die";
        return replaceMsg;
    }
};

JSON.stringify(info);
//出si了,返回的是:'"{"msg":"Go Die"}"',说好的疏忽函数呢

这个函数就是如许子的。

实在Date范例能够直接传给JSON.stringify做参数,个中的原理就是,Date范例内置了toJSON要领。

四、小结以及关于兼容性的题目

到这里终究把,JSON和JS中的JSON,梳理了一遍,也对内里的细节和注重点举行了一次遍历,晓得JSON是一种语法上衍生于JS言语的一种轻量级的数据交流花样,也邃晓了JSON相关于平常的JS数据结构(特别是对象)的差异,更进一步,细致地议论了JS中关于JSON处置惩罚的3个函数和细节。

不过遗憾的是,以上所用的3个函数,不兼容IE7以及IE7之前的浏览器。有关兼容性的议论,留待以后吧。假如想直接在运用上处理兼容性,那末能够套用JSON官方的js,能够处理。

若有马虎,迎接留言指出。

    原文作者:StinsonZhao
    原文地址: https://segmentfault.com/a/1190000008832185
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞