# stringify
stringify
ify---为后缀, 使成,使……化- stringify:字符串化
ize---为后缀,做成,变成,……化- serial --> serialize:序列化 :::
/**
* Converts a JavaScript Object Notation (JSON) string into an object.
* @param text A valid JSON string.
* @param reviver A function that transforms the results. This function is called for each member of the object.
* If a member contains nested objects, the nested objects are transformed before the parent object is.
*/
parse(text: string, reviver?: (this: any, key: string, value: any) => any): any;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
* @param value A JavaScript value, usually an object or array, to be converted.
* @param replacer A function that transforms the results.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
* @param value A JavaScript value, usually an object or array, to be converted.
* @param replacer An array of strings and numbers that acts as a approved list for selecting the object properties that will be stringified.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# JSON.stringify() 的 5 个秘密特性
作为一名 JavaScript 开发人员,JSON.stringify() 是用于调试的最常见函数。但是它的作用是什么呢,难道我们不能使用 console.log() 来做同样的事情吗?让我们试一试。
//初始化一个 user 对象
const user = {
name: "Prateek Singh",
age: 26,
};
console.log(user);
// 结果
// [object Object]
2
3
4
5
6
7
8
9
console.log() 没有帮助我们打印出期望的结果。它输出 [object Object],因为从对象到字符串的默认转换是 [object Object]。因此,我们使用 JSON.stringify() 首先将对象转换成字符串,然后在控制台中打印,如下所示。
const user = {
name: "Prateek Singh",
age: 26,
};
console.log(JSON.stringify(user));
// 结果
// "{ "name" : "Prateek Singh", "age" : 26 }"
2
3
4
5
6
7
8
9
# stringify 基本用法
JSON.stringify 方法接受一个参数并将其转换成JSON 字符串形式。
const firstItem = {
title: "Transformers",
year: 2007,
};
JSON.stringify(firstItem);
// {'title':'Transformers','year':2007}
2
3
4
5
6
7
当出现一个不能序列化成 JSON 字符串的元素时就会出现问题 。
const secondItem = {
title: "Transformers",
year: 2007,
starring: new Map([
[0, "Shia LaBeouf"],
[1, "Megan Fox"],
]),
};
JSON.stringify(secondItem);
// {'title':'Transformers','year':2007,'starring':{}}
2
3
4
5
6
7
8
9
10
11
# 第二个参数
TIP
如果指定了 replacer 是一个函数,则可以选择性地替换值,或者如果指定了 replacer 是一个数组,则可选择性地仅包含数组指定的属性。
JSON.stringify(secondItem, ["title"]);
// {'title':'Transformers'}
2
我们可以筛掉一些不想要的值。
这些值要么太大(比如 Error 对象),要么无法转成可读的JSON形式。
替换器参数也可以是一个函数。该函数接受 JSON.stringify 方法遍历对象时当前的属性和值作为参数。
如果函数不返回任何值或者返回 undefined,当前节点就不会出现在结果里。
JSON.stringify(secondItem, (key, value) => {
if (value instanceof Map) {
return [...value.values()];
}
return value;
});
// {'title':'Transformers','year':2007,'starring':['Shia LaBeouf','Megan Fox']}
2
3
4
5
6
7
通过让函数返回 undefined,可以在结果里删除这些属性。
JSON.stringify(secondItem, (key, value) => {
if (typeof value === "string") {
return undefined;
}
return value;
});
// {"year":2007,"starring":{}}
2
3
4
5
6
7
第二个参数还可以用来创建简单的对象哈希函数。
属性顺序
但有一点要注意,JSON.stringify(obj)不能保证属性的输出顺序,当序列化的结果用于哈希/校验和的时,这点至关重要。
为此,我们可以把第二个参数设置为 Object.keys(obj).sort(),对象将会以这个顺序序列化。
function objectHash(obj: object): string {
const str = JSON.stringify(obj, Object.keys(obj).sort());
return createHash("sha1")
.update(str)
.digest("hex");
}
2
3
4
5
6
# 第三个参数
第三个参数设置最终字符串里的空白缩进。
- 如果参数是一个数字,那么序列化的每个层级都会用这个数量的空格符缩进。
JSON.stringify(secondItem, null, 2);
//{
// "title": "Transformers",
// "year": 2007,
// "starring": {}
//}
2
3
4
5
6
- 如果第三个参数是字符串,就会替代空格符。
JSON.stringify(secondItem, null, "🦄");
//{
//🦄"title": "Transformers",
//🦄"year": 2007,
//🦄"starring": {}
//}
2
3
4
5
6
# toJSON 方法
如果我们序列化的对象有一个 toJSON 方法,它可以作为任意对象的属性,它将会采用自定义的序列化过程。你可以在方法里返回一个新的值,这个值将会替换原始对象被序列化。
const thirdItem = {
title: "Transformers",
year: 2007,
starring: new Map([
[0, "Shia LaBeouf"],
[1, "Megan Fox"],
]),
toJSON() {
return {
name: `${this.title} (${this.year})`,
actors: [...this.starring.values()],
};
},
};
console.log(JSON.stringify(thirdItem));
// {"name":"Transformers (2007)","actors":["Shia LaBeouf","Megan Fox"]}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 第一大特性
对于 undefined、任意的函数以及 symbol 三个特殊的值分别作为对象属性的值、数组元素、单独的值时 JSON.stringify()将返回不同的结果。
const data = {
a: "aaa",
b: undefined,
c: Symbol("dd"),
fn: function() {
return true;
},
};
JSON.stringify(data); // 输出:?
// "{"a":"aaa"}"
2
3
4
5
6
7
8
9
10
11
# stringify 存在的坑
let obj = {
name: "Gopal",
age: Infinity,
};
let originObj = JSON.stringify(obj);
console.log(originObj); // {"name":"Gopal","age":null}
// Infinity 变成了 null
2
3
4
5
6
7
8
- 解决方法 1:
- 简单粗暴,重新给 age 属性赋值
- 解决方法 2:
function censor(key, value) {
if (value === Infinity) {
return "Infinity";
}
return value;
}
var b = JSON.stringify(a, censor);
var c = JSON.parse(b, function(key, value) {
return value === "Infinity" ? Infinity : value;
});
2
3
4
5
6
7
8
9
10
11
# 转换属性值中有 toJSON 方法,慎用
转换值中如果有 toJSON 方法,该方法返回的值将会是最后的序列化结果
// toJSON
let toJsonMyIntro = {
name: "Gopal",
age: 25,
like: "FE",
toJSON: function () {
return "前端杂货铺";
},
};
console.log(JSON.stringify(toJsonMyIntro)); // "前端杂货铺"
2
3
4
5
6
7
8
9
10
11
12
13
# 被转换值中有 undefined、任意的函数以及 symbol 值,慎用
- 一种是数组对象,undefined、任意的函数以及 symbol 值会被转换成 null
JSON.stringify([undefined, Object, Symbol("")]);
// '[null,null,null]'
2
- 一种是非数组对象,在序列化的过程中会被忽略
JSON.stringify({ x: undefined, y: Object, z: Symbol("") });
// '{}'
2
对于这种情况,我们可以使用 JSON.stringify 的第二个参数,使其达到符合我们的预期
const testObj = { x: undefined, y: Object, z: Symbol("test") };
const resut = JSON.stringify(testObj, function(key, value) {
if (value === undefined) {
return "undefined";
} else if (typeof value === "symbol" || typeof value === "function") {
return value.toString();
}
return value;
});
console.log(resut);
// {"x":"undefined","y":"function Object() { [native code] }","z":"Symbol(test)"}
2
3
4
5
6
7
8
9
10
11
12
13
# 包含循环引用的对象,慎用
let objA = {
name: "Gopal",
};
let objB = {
age: 25,
};
objA.age = objB;
objB.name = objA;
JSON.stringify(objA);
2
3
4
5
6
7
8
9
10
11
会报以下错误:
Uncaught TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'age' -> object with constructor 'Object'
--- property 'name' closes the circle
at JSON.stringify (<anonymous>)
at <anonymous>:1:6
2
3
4
5
6
# 以 symbol 为属性键的属性,慎用
所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
JSON.stringify({ [Symbol.for("foo")]: "foo" }, [Symbol.for("foo")]);
// '{}'
JSON.stringify({ [Symbol.for("foo")]: "foo" }, function(k, v) {
if (typeof k === "symbol") {
return "a symbol";
}
});
// undefined
2
3
4
5
6
7
8
9
# 值为 NaN 和 Infinity,慎用
数组的值,或者非数组对象属性值为 NaN 和 Infinity 的,会被转换成 null
let me = {
name: "Gopal",
age: Infinity,
money: NaN,
};
let originObj = JSON.stringify(me);
console.log(originObj); // {"name":"Gopal","age":null,"money":null}
JSON.stringify([NaN, Infinity]);
// [null,null]
2
3
4
5
6
7
8
9
10
# 具有不可枚举的属性值时,慎用
不可枚举的属性默认会被忽略:
let person = Object.create(null, {
name: { value: "Gopal", enumerable: false },
age: { value: "25", enumerable: true },
});
console.log(JSON.stringify(person));
// {"age":"25"}
2
3
4
5
6
7