cc
cc copied to clipboard
64.深度使用 JSON.stringify()
深度使用 JSON.stringify()
按照 JSON 的规范,使用 JSON.stringify() 做对象序列化时,如果一个属性为函数,那这个属性就会被忽略。
const data1 = {
a: 'aaa',
fn: function() {
return true
}
}
JSON.stringify(data)
// 结果是 "{"a":"aaa"}"
还有一种情况,一个属性的值为 undefined
const data2 = {
a: 'abc',
b: undefined
}
JSON.stringify(data2)
// 结果是 "{"a":"abc"}"
如果属性为 null 则可以正常序列化这个属性:
const data3 = {
a: 'abc',
b: null
}
JSON.stringify(data3)
// 结果是 "{"a":"abc","b":null}"
因为 null 可表示已经赋值,而 undefined 表示未定义、未赋值,所以执行 JSON.stringify 不会处理。
stringify 函数
stringify 函数的定义为 JSON.stringify(value [, replacer [, space]])
后面还带有我不常用两个可选参数 replacer 和 space
value 参数不多解释,replacer 其实就是一个自定义函数,可以改变 JSON.stringify 的行为,space 就是格式化输出,最大值为 10,非整数时取值为 1
stringify 输出 Function
本质上无论怎么改,stringify 还是不会输出 Function,但是 Function 可以调用 toString() 方法的,所以思路就很明了了。
const data1 = {
a: 'aaa',
fn: function() {
return true
}
}
const replace = function(k ,v) {
if(typeof v === 'function') {
return Function.prototype.toString.call(v)
}
return v
}
JSON.stringify(data1, replace)
// 结果 "{"a":"aaa","fn":"function () {\n return true\n }"}"
同理可证 undefined 也能输出了
const replace = function(k ,v) {
if(v === undefined){
return 'undefined'
}
return v
}
stringify 格式化输出
JSON.stringify 的第三个参数很简单,相当于我们编辑器的 tab_size
const data4 = {
a: 'abc',
b: null,
c: {
x: 'xxx',
y: 'yyy',
z: 2046
},
d: 9527
}
JSON.stringify(data4, null, 2);
// 输出结果
/*
"{
"a": "abc",
"b": null,
"c": {
"x": "xxx",
"y": "yyy",
"z": 2046
},
"d": 9527
}"
*/
toJSON 方法
toJSON 是个覆盖函数,尽量少用,看了代码就懂了:
const data5 = {
a: 'abc',
b: null,
c: {
x: 'xxx',
y: 'yyy',
z: 2046
},
d: 9527,
toJSON: function() {
return 'WTF'
}
}
JSON.stringify(data5, null, 2);
// 结果返回 "WTF"
直接覆盖掉所有的序列化处理,返回了 "WTF"
下面的评论中 @yingyuk 给出了详细的 stringify转换demo
推荐看我的这篇文章:JSON
使用JSON.stringify()和JSON.parse()做对象的深拷贝:
var data = {
a: 'abc',
c: {
x: {
foo: 'xxx',
bar: 'yyy'
},
y: 'yyy'
}
};
var copy = JSON.stringify(data);
var copyObj = JSON.parse(copy);
@xwHoward 所提到的深拷贝方法会忽略undefined和function,所以需要采用文章的方法再次封装 - -
@xwHoward 这种深拷贝方式有什么副作用吗
直接用扩展运算符
@Sakuyakun 副作用就是文中提到的undefined和function会忽略咯,还有就是不支持JSON的浏览器就用不了
比较有意思
@lulusir 还有如果字符串中含有'\n',JSON.parse解析是会报错的
@honpery @Sakuyakun @lulusir @xxyj 确实这种方法有一定局限性,但在对数据结构明了、性能要求比较高(相比用递归进行对象复制)的时候可以用这种方式快速进行对象深拷贝,算是一种trick啦
很好
赞 可以的
@xwHoward 按我的了解,该方法复制对象的性能比直接使用传统方式复制对象更慢。 https://jsperf.com/deep-copy-vs-json-stringify-json-parse/10
确实这种方法有一定局限性,但在对数据结构明了、性能要求比较高(相比用递归进行对象复制)的时候可以用这种方式快速进行对象深拷贝,算是一种trick啦
@lulusir 瀏覽器支援不是問題了。

var myMap = new Map();
myMap.set(0, 'zero');
var mySet = new Set();
mySet.add(1);
class test {
constructor(opt) {
this.opt = opt;
}
}
JSON.stringify({
String: 'string',
Boolean: true,
Number: 123,
NaN: NaN, // null,
Infinity: Infinity, // null
null: null, // null
undefined: undefined, // 没有 key
Array: [1, 2, 3],
Object: {
foo: 'bar',
},
Symbol: Symbol('foo'), // {}
Map: myMap, // {}
Set: mySet, // {}
Promise: new Promise(function(resolve, reject) {}), // {}
Proxy: new Proxy({}, {}), // {}
Class: test, // 没有 key
ClassA: new test(123), // { "opt": 123 },
Math: Math, // {}
// Buffer: new Buffer('123'), // nodeJs '{"Buffer":{"type":"Buffer","data":[49,50,51]}}'
Error: new Error('error'), // {}
Function: function() { // 没有 key
console.info('hi');
},
Date: new Date(), // "2017-08-01T13:52:48.628Z",
RegExp: /RegExp/, // {}
}, null, 2);
/*
"{
"String": "string",
"Boolean": true,
"Number": 123,
"NaN": null,
"Infinity": null,
"null": null,
"Array": [
1,
2,
3
],
"Object": {
"foo": "bar"
},
"Map": {},
"Set": {},
"Promise": {},
"Proxy": {},
"ClassA": {
"opt": 123
},
"Math": {},
"Error": {},
"Date": "2017-08-01T14:04:58.522Z",
"RegExp": {}
}"
*/