>  기사  >  웹 프론트엔드  >  JSON에 대한 깊은 이해

JSON에 대한 깊은 이해

巴扎黑
巴扎黑원래의
2017-04-29 14:41:47986검색

먼저 JS 개체를 JSON 문자열로 직렬화하는 것과 관련된 JS의 일반적인 문제를 살펴보겠습니다. JSON.stringify를 전달한 후 다음 JS 개체의 문자열은 어떻게 보일까요? 성급하게 복사하여 콘솔에 붙여넣지 마십시오. 먼저 코드 편집기나 문서를 열고 작성하고 읽은 다음 작성한 후 콘솔 출력을 주의 깊게 비교하십시오. 실수가 있으면 전체 텍스트와 주석을 읽으십시오. 하하.

아아아아

두 번째 질문입니다. 최종 JSON 문자열에서 '친구'의 이름을 모두 대문자로 변경하려면, 즉 "Good"을 "GOOD"로, "Man"을 "MAN"으로 변경하려면 어떻게 해야 할까요? 해?

위의 두 가지 질문을 바탕으로 다시 원본으로 돌아가 JSON이 정확히 무엇인지 질문해 보겠습니다. JSON이 데이터 교환하기 쉬운 이유는 무엇입니까? JSON과 JS 객체의 차이점은 무엇입니까? JS, JSON.parse, JSON.stringify 및 흔하지 않은 toJSON에서 이러한 함수의 매개변수와 처리 세부정보는 무엇입니까?

이번 "JSON Deep Journey"에 오신 것을 환영합니다. 아래에서는 다음과 같은 측면에서 JSON을 이해하겠습니다.

  • 첫 번째는 "JSON은 경량 데이터 교환 형식입니다"라는 이해입니다. 그럼 자주 혼동되는 JSON과 JS 객체의 차이점을 살펴보세요. 마지막으로 JS에서 이러한 JSON 관련 함수의 구체적인 실행 세부 사항을 살펴보겠습니다.

  • 전체 텍스트가 저처럼 JSON에 대해 거의 모르는 사람들이 JSON이 무엇인지 명확하게 설명하고, JSON을 능숙하게 사용할 수 있고, JS 객체가 JSON 문자열로 직렬화되면 보지 않고도 출력이 무엇인지 알 수 있도록 도움이 되기를 바랍니다. 콘솔에서.

  • 1. JSON은 텍스트 기반 형식으로 경량보다 우수하며 데이터 교환에 사용됩니다

  • JSON의 공식 소개를 보지 않으셨다면 여기로 오세요. 공식 소개의 첫 번째와 두 번째 단락에서 JSON이 무엇인지 명확하게 설명했습니다.
  • 1. 데이터 형식

    형식이란 무엇입니까? 데이터가 어떻게 표현되어야 하는지를 표준화하는 것입니다. 예를 들어, 키가 "160cm"이고 몸무게가 "60kg"인 "Two Hundred Six"라는 사람이 있습니다. 이제 이 사람의 정보를 다른 사람에게 전달하려고 합니다. 또는 다른 것들도 있습니다.
  • 이름 "이백육", 키 "160cm", 몸무게 "60kg"

name="이백육"&height="160cm"&weight="60kg"

  • 이백육16060


  • {"이름":"二百六","키":160,"체중":60}


  • ... ...

  • 위의 선택 항목 모두 전송되는 데이터는 동일하지만, 형태는 다양할 수 있음을 알 수 있는데, 이는 다양한 형식의 데이터이며, 표현 방법 중 하나가 JSON입니다.

    2. 텍스트 기반 데이터 형식
  • JSON은 바이너리 기반 데이터가 아닌 텍스트 기반 데이터 형식이므로 JSON이 전달될 때 JSON 형식을 준수하는 문자열을 전달합니다(JSON 형식이 무엇인지에 대해서는 다음에서 설명합니다). 두 번째 부분) 우리는 종종 "JSON 문자열"이라고 부릅니다.

    3. 경량 데이터 형식
  • JSON 이전에는 xml이라는 데이터 형식이 있었는데, 이는 오늘날에도 널리 사용되지만 JSON은 더 가볍습니다. 예를 들어 xml에는 많은 태그가 필요합니다. 위의 예를 보면 태그 자체가 xml을 차지하고 있음을 알 수 있습니다. 형식 데이터는 많은 공간을 차지하며 JSON은 상대적으로 가볍습니다. 즉, 동일한 데이터가 JSON 형식에서 더 적은 대역폭을 차지하므로 많은 양의 데이터를 요청하고 전송할 때 확실한 이점이 있습니다.


    4. 데이터 교환에 널리 사용됩니다

  • 경량화는 이미 데이터 교환의 이점이지만 더 중요한 것은 JSON이 읽기, 쓰기 및 기계 구문 분석이 쉽다는 것입니다. 즉, 이 JSON은 인간과 기계 모두에게 친화적이며 가볍고 언어 독립적입니다(텍스트 기반이기 때문입니다). ), 따라서 JSON은 데이터 교환에 널리 사용됩니다.
  • Ajax POST 요청을 수행하는 프런트엔드 JS와 요청을 처리하는 백엔드 PHP를 예로 들어보겠습니다.

프런트 엔드는 JS 개체를 구성하여 전송할 데이터를 래핑한 다음 JS 개체를 JSON 문자열로 변환한 다음 요청을 백 엔드로 보냅니다.

백엔드 PHP는 이 JSON 문자열을 수신하고 JSON 문자열을 PHP 객체로 변환한 다음 요청을 처리합니다.

보시다시피, 동일한 데이터에는 세 가지 다른 표현 형식이 있습니다. 즉, 프런트엔드 JS 객체, 전송된 JSON 문자열, 백엔드 PHP 객체와 JS 객체는 분명히 동일한 것이 아닙니다. JSON은 누구나 데이터를 전송하는 데 사용되기 때문에 누구나 이 데이터 형식을 이해할 수 있고, JSON 데이터 형식을 자신이 이해할 수 있는 데이터 구조로 쉽게 변환할 수 있다는 점은 다양한 언어 환경에서 데이터를 교환하는 데 편리합니다.

2. 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. JS 객체에 비해 JSON 형식의 엄격한 구문은 무엇입니까

먼저 "키-값 쌍으로 표현되는 객체" 형태로 둘의 차이점을 비교해 보겠습니다. JSON을 다른 형태로 표현하는 방법은 비교 후 나열하겠습니다.

JSON JS 객체
콘텐츠 비교
키 이름
对比内容 JSON JS对象

键名

必须是加双引号

可允许不加、加单引号、加双引号

属性值

只能是数值(10进制)、字符串(双引号)、布尔值和null,
也可以是数组或者符合JSON要求的对象,
不能是函数、NaN, Infinity, -Infinity和undefined

爱啥啥

逗号问题

最后一个属性后面不能有逗号

可以

数值

前导0不能用,小数点后必须有数字

没限制

큰따옴표로 묶어야 합니다 작은따옴표나 큰따옴표 없이 허용될 수 있습니다.
속성값 숫자(십진수), 문자열(큰따옴표), 부울 값 및 null만 가능합니다.
JSON 요구 사항을 충족하는 배열이나 개체일 수도 있습니다.
함수, NaN, Infinity, -Infinity 및 정의되지 않음일 수 없습니다.
모든 것을 사랑합니다
쉼표 문제 마지막 속성 뒤에는 쉼표를 사용할 수 없습니다.
가치 선행 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验证。

  这第二个参数若是数组

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方法。

4. 호환성에 관한 요약 및 문제점

이 시점에서 드디어 JS에서 JSON과 JSON을 정리하고 세부 사항과 주의 사항을 살펴보았습니다. JSON이 JS 언어에서 문법적으로 파생된 경량 데이터 교환 형식이라는 것을 알고 차이점도 이해했습니다. JSON과 일반 JS 데이터 구조(특히 객체) 사이를 다루고, JS에서 JSON 처리의 세 가지 기능과 세부 사항에 대해 자세히 논의합니다.

아쉽게도 위에 사용된 세 가지 기능은 IE7과 IE7 이전 브라우저에서는 호환되지 않습니다. 호환성에 대한 논의는 나중에 남겨두겠습니다. 호환성 문제를 애플리케이션에서 직접 해결하고 싶다면 JSON 공식 js를 적용하면 해결이 가능합니다.

틀린 부분이 있으면 지적할 수 있도록 메시지를 남겨주세요.

위 내용은 JSON에 대한 깊은 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.