语法
- JSON.stringify(value[, replacer [, space]])
参数
- value:将要序列化成 一个 JSON 字符串的值
- replacer【可选】:如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。
- space【可选】:指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为 10。该值若小于 1,则意味着没有空格;如果该参数为字符串(当字符串长度超过 10 个字母,取其前 10 个字母),该字符串将被作为空格;如果该参数没有提供(或者为 null),将没有空格。
返回值
异常
- 当在循环引用时会抛出异常TypeError ("cyclic object value")(循环对象值)
- 当尝试去转换 BigInt 类型的值会抛出TypeError ("BigInt value can't be serialized in JSON")(BigInt 值不能 JSON 序列化)
转换规则
JSON.stringify()将值转换为相应的 JSON 格式:
- 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
- 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
- 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
- undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined).
- 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
- 所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
- Date 日期调用了 toJSON() 将其转换为了 string 字符串(同 Date.toISOString()),因此会被当做字符串处理。
- NaN 和 Infinity 格式的数值及 null 都会被当做 null。
- 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
自己实现的方法
const myJsonStringify = function (source, replacer, space) {
// space功能暂时未实现
let result = source
// 检测replacer是否存在,存在且不是函数和数组则直接忽略,函数或者数组则对数据做相应处理
const replacerType = checkOfData(replacer)
if (replacerType === 'function') {
result = replacer(source)
}
// 判断值的类型并做对应的处理
switch (checkOfData(result)) {
case 'number': case 'null':
if (
Number.isNaN(source) ||
source === Infinity ||
source === -Infinity ||
source === null
) {
result = 'null'
} else {
result = `${source}`
}
break
case 'boolean':
result = `${source.valueOf()}`
break
case 'undefined' || 'function' || 'symbol':
result = undefined
break
case 'string':
result = `"${source}"`
break
case 'object':
if (source.toJSON && typeof source.toJSON === 'function') {
result = myJsonStringify(source.toJSON(), replacer, space)
} else {
result = []
Object.keys(source).forEach((item, index) => {
if (typeof item !== 'symbol') {
if (
!['symbol', 'function', 'undefined'].includes(typeof source[item])
) {
if (source[item] === source) {
throw new Error('cyclic object value')
}
if (
(replacerType === 'array' && replacer.includes(item)) ||
replacerType !== 'array'
) {
result.push(`"${item}":${myJsonStringify(source[item])}`)
}
}
}
})
result = `{${result}}`.replace("'/g", '"')
}
break
case 'array':
result = []
source.forEach((item, index) => {
if (['symbol', 'function', 'undefined'].includes(typeof item)) {
result[index] = 'null'
} else {
result[index] = myJsonStringify(item)
}
})
result = `[${result}]`
result = result.replace("'/g", '"')
break
case 'bigint':
throw new Error("BigInt value can't be serialized in JSON")
default: // Set,Map等数据类型直接返回{}
result = '{}'
break
}
return result
}
测试示例
// 验证代码
const log = console.log
log(myJsonStringify({}) === JSON.stringify({})) // true
// myJsonStringify({}) === JSON.stringify({}) // true
// myJsonStringify(true) === JSON.stringify(true) // true
// myJsonStringify('foo') === JSON.stringify('foo') // true
// myJsonStringify([1, 'false', false]) === JSON.stringify([1, 'false', false]) // true
// myJsonStringify({x: 5}) === JSON.stringify({x: 5}) // true
// myJsonStringify({x: 5, y: 6}) === JSON.stringify({x: 5, y: 6}) // true
// myJsonStringify([new Number(1), new String('false'), new Boolean(false)]) === JSON.stringify([new Number(1), new String('false'), new Boolean(false)]) '[1,"false",false]' // true
// myJsonStringify({x: undefined, y: Object, z: Symbol('')})===JSON.stringify({x: undefined, y: Object, z: Symbol('')}) // true
// myJsonStringify({x: undefined, y: Object, z: Symbol('')}) === JSON.stringify({x: undefined, y: Object, z: Symbol('')}) // true
// myJsonStringify([undefined, Object, Symbol('')])===JSON.stringify([undefined, Object, Symbol('')]) // true
// myJsonStringify({[Symbol('foo')]: 'foo'}) === JSON.stringify({[Symbol('foo')]: 'foo'}) // true
// myJsonStringify({[Symbol.for('foo')]: 'foo'}, [Symbol.for('foo')]) === JSON.stringify({[Symbol.for('foo')]: 'foo'}, [Symbol.for('foo')]) // true
/***
myJsonStringify({[Symbol.for('foo')]: 'foo'}, function (k, v) {
if (typeof k === 'symbol') {
return 'a symbol'
}
}) ===
JSON.stringify({[Symbol.for('foo')]: 'foo'}, function (k, v) {
if (typeof k === 'symbol') {
return 'a symbol'
}
}) // true
*/
/**
myJsonStringify(
Object.create(null, {
x: {value: 'x', enumerable: false},
y: {value: 'y', enumerable: true}
})
) ===
JSON.stringify(
Object.create(null, {
x: {value: 'x', enumerable: false},
y: {value: 'y', enumerable: true}
})
) // true
*/