
JSON.stringify es un método que se utiliza a menudo en el desarrollo diario. ¿Realmente se puede utilizar de forma flexible?
Antes de estudiar este artículo, Xiaobao quiere que todos respondan algunas preguntas y aprendan stringify en profundidad.
stringify tiene varios parámetros. ¿Cuál es el uso de cada parámetro?stringify null、undefined、NaN ?ES6 ¿Habrá un tratamiento especial durante el proceso de serialización del tipo de Symbol y BigInt ?stringify no es adecuado para copia profunda?stringify ?El contexto de todo el artículo es coherente con el mapa mental a continuación. Puede dejar una impresión primero.

En la programación diaria, a menudo usamos el método JSON.stringify para convertir un objeto en una cadena JSON .
constante stu = {
nombre: 'zcxiaobao',
edad: 18
}
// {"nombre":"zcxiaobao","edad":18}
console.log(JSON.stringify(stu)); ¿Pero es realmente tan simple stringify ? Primero echemos un vistazo a la definición de stringify en MDN .
MDN dice: El método JSON.stringify() convierte un objeto o valor JavaScript en JSON . Si se especifica una función replacer , el valor se puede reemplazar opcionalmente, o el replacer especificado es una matriz. .
Después de leer la definición, Xiaobao se sorprendió stringfy Por supuesto, stringify tiene tres parámetros.
Echemos un vistazo a la sintaxis stringify y la introducción de los parámetros:
JSON.stringify(valor[, sustituto [, espacio]])
value : el valor que se secuenciará en una cadena JSON.replacer (opcional) Si el parámetro es una función , durante el proceso de serialización, cada atributo del valor serializado será convertido y procesado por la función
si el parámetro es una matriz , solo las propiedades contenidas en esta matriz serán Solo el atributo; Los nombres en se serializarán en la cadena JSON final.
Si este parámetro es null o no se proporciona, se serializarán todos los atributos del objeto.
space (opcional): especifica la cadena de espacios en blanco utilizada para la sangría, que se utiliza para embellecer la salida. Si el parámetro es un número, representa el número de espacios. El límite superior es 10.
Si
el valor es menor que 1, significa que no hay espacios.
Si el parámetro es una cadena (cuando la longitud de la cadena excede las 10 letras, se toman las primeras 10 letras), la cadena se tratará como espacios.
el parámetro no se proporciona (o es nulo), no habrá espacios
. Probemos el uso de replacer .
replacer como función
replacer como función, tiene dos parámetros, clave ( key ) y valor ( value ), y ambos parámetros se serializarán.
Al principio, la función de reemplazo se pasará en una cadena vacía como valor clave, que representa el objeto que se va a encadenar . Es importante entender esto. La función replacer no analiza el objeto en pares clave-valor cuando aparece, sino que primero pasa el objeto que se va a serializar . Luego, las propiedades de cada objeto o matriz se pasan secuencialmente. Si el valor de retorno de la función no está definido o es una función, el valor del atributo se filtrará y el resto seguirá las reglas de retorno.
// repalcer acepta el valor clave de dos parámetros
// valor clave es cada par clave-valor del objeto // por lo que podemos simplemente filtrar según el tipo de clave o valor function replacement(key, value) {
if (tipo de valor === "cadena") {
devolver indefinido;
}
valor de retorno;
}
// la función puede probar la función por sí misma replacementrFunc(key, value) {
if (tipo de valor === "cadena") {
retorno () => {};
}
valor de retorno;
}
const foo = {fundación: "Mozilla", modelo: "caja", semana: 45, transporte: "coche", mes: 7};
const jsonString = JSON.stringify(foo, replacement); JSON es {"week":45,"month":7}
pero si la serialización es una matriz, si replacer devuelve undefined o función, el valor actual no se ignorará y será reemplazado por null .
lista constante = [1, '22', 3] const jsonString = JSON.stringify(list, replacement)El resultado de la serialización JSON es
replacer
JSON [1,null,3]',
que es más fácil de entender como una matriz, filtrando los valores clave que aparecen en la matriz.
const foo = {fundación: "Mozilla", modelo: "caja", semana: 45, transporte: "coche", mes: 7};
const jsonString = JSON.stringify(foo, ['week', 'month']); El resultado de la serialización JSON es {"week":45,"month":7} y solo week y month son retenido.
undefined
undefined los valores Symbol se ignorarán durante el proceso de serialización.
y los valores Symbol se ignorarán cuando se conviertan a nulos
solo: se devolverá indefinido
// 1. Se ignorará la existencia de estos tres valores en el valor del atributo del objeto const obj = {.
nombre: 'zc',
edad: 18,
// La función será ignorada sayHello() {
console.log('hola mundo')
},
// indefinido será ignorado esposa: indefinido,
// El valor del símbolo se ignorará id: Símbolo(111),
// [Símbolo('zc')]: 'zc',
}
// Resultado de salida: {"name":"zc","age":18}
console.log(JSON.stringify(obj));
// 2. Estos tres valores de la matriz se convertirán en nulos
lista constante = [
'zc',
18,
// Función convertida a nula
función decirHola() {
console.log('hola mundo')
},
// indefinido convertido a nulo
indefinido,
// Símbolo convertido a nulo
Símbolo(111)
]
// ["zc",18,nulo,nulo,nulo]
console.log(JSON.stringify(lista))
// 3. La conversión separada de estos tres valores devolverá indefinido
console.log(JSON.stringify(indefinido)) // indefinido
console.log(JSON.stringify(Símbolo(111))) // indefinido
console.log(JSON.stringify(función decirHola() {
console.log('hola mundo')
})) // convierte el valor. Si hay toJSON() , cualquier valor que devuelva toJSON() será el valor que devuelva el resultado de la serialización, y los otros valores serán. ignorado.
objeto constante = {
nombre: 'zc',
a JSON(){
devolver 'volver a JSON'
}
}
// volver a JSON
console.log(JSON.stringify(obj)); valores, números y cadenas booleanos se convertirán automáticamente al valor original correspondiente JSON durante el proceso de serialización.
stringify([nuevo Número(1), nueva Cadena("zcxiaobao"), nuevo Booleano(verdadero)]);
// [1,"zcxiaobao",true] La característica cuatro está dirigida principalmente a valores especiales en JavaScript , como NaN , Infinity y null en el tipo Number . Estos tres tipos de valores se tratarán como null durante la serialización .
// [nulo,nulo,nulo,nulo,nulo]
JSON.stringify([nulo, NaN, -NaN, Infinito, -Infinito])
// La característica 3 mencionó que los objetos empaquetados de valores booleanos, números y cadenas se convertirán automáticamente a los valores originales correspondientes durante el proceso de serialización // La conversión de tipo implícita llamará a la clase de empaquetado, por lo que Número => NaN será llamado primero
// Luego convertir a nulo
// 1/0 => Infinito => nulo
JSON.stringify([Number('123a'), +'123a', 1/0]) El método toJSON (igual que Date.toISOString() ) se implementa en el objeto Date para convertirlo en un cadena, por lo que JSON.stringify() serializará el valor de fecha en una cadena de formato de hora .
// "2022-03-06T08:24:56.138Z" JSON.stringify(new Date())
cuando se menciona la característica Símbolo, cuando Symbol se usa como valor, los objetos, matrices y usos individuales se ignorarán, se convertirán a null y se convertirán a undefined respectivamente.
Del mismo modo, todas las propiedades con Símbolo como clave de propiedad se ignorarán por completo, incluso si se fuerza su inclusión en el parámetro de reemplazo .
objeto constante = {
nombre: 'zcxiaobao',
edad: 18,
[Símbolo('lyl')]: 'único'
}
reemplazo de función (clave, valor) {
if (tipo de clave === 'símbolo') {
valor de retorno;
}
}
// indefinido
JSON.stringify(obj, replacer); Del caso anterior, podemos ver que aunque especificamos a la fuerza el valor del tipo Symbol de retorno mediante replacer , eventualmente será ignorado.
JSON.stringify estipula: Intentar convertir un valor de tipo BigInt arrojará TypeError
const bigNumber = BigInt(1) // TypeError no detectado: no sé cómo serializar un BigInt Console.log(JSON.stringify(bigNumber))
La característica 8 señala: La ejecución de este método en objetos que contienen referencias circulares (los objetos se refieren entre sí, formando un bucle infinito) generará un error
de copia profunda de desarrollo diario
.La forma más simple y violenta es usar JSON.parse(JSON.stringify(obj)) , pero la copia profunda con este método tiene enormes dificultades. El problema clave es que stringify no puede manejar el problema de la referencia circular.
objeto constante = {
nombre: 'zcxiaobao',
edad: 18,
}
objeto de bucle constante = {
objeto
}
// Forma una referencia circular obj.loopObj = loopObj;
JSON.stringify(obj)
/* TypeError no detectado: conversión de estructura circular a JSON
--> comenzando en el objeto con el constructor 'Objeto'
| propiedad 'loopObj' -> objeto con constructor 'Objeto'
--- la propiedad 'obj' cierra el círculo
en JSON.stringify (<anónimo>)
en <anónimo>:10:6
*/ la serialización de propiedades enumerables para objetos (incluido Map/Set/WeakMap/WeakSet ), además de algunas de las situaciones mencionadas anteriormente, stringify también estipula claramente que solo se serializarán propiedades enumerables
// No enumerables las propiedades se ignorarán de forma predeterminada // {"age":18}
JSON.stringify(
Objeto.create(
nulo,
{
nombre: {valor: 'zcxiaobao', enumerable: falso},
edad: {valor: 18, enumerable: verdadero}
}
)
); El objeto localStorage se utiliza para guardar los datos de todo el sitio web durante mucho tiempo. Los datos guardados no tienen tiempo de caducidad hasta que se eliminan manualmente. Normalmente lo almacenamos en forma de objetos.
Simplemente llame al método del objeto localStorage
const obj = {
nombre: 'zcxiaobao',
edad: 18
}
// Simplemente llama a localStorage.setItem()
localStorage.setItem('zc', obj);
//El resultado final devuelto es [objeto Objeto]
// Se puede ver que simplemente llamar a localStorage falla console.log(localStorage.getItem('zc')) localStorage coopera con JSON.stringify
localStorage.setItem('zc', JSON.stringify(obj));
// El resultado final devuelto es {nombre: 'zcxiaobao', edad: 18}
Console.log(JSON.parse(localStorage.getItem('zc'))) supone tal escenario. El backend devuelve un objeto largo con muchos atributos, y solo necesitamos algunos de ellos. atributos en localStorage .
Opción 1: Asignación de desestructuración + stringify
// Solo necesitamos los atributos a, e, f const obj = {
a:1, b:2, c:3, d:4, e:5, f:6, g:7
}
// Asignación de desestructuración const {a,e,f} = obj;
// Almacenar en almacenamiento local
localStorage.setItem('zc', JSON.stringify({a,e,f}))
// {"a":1,"e":5,"f":6}
console.log(localStorage.getItem('zc')) usa el parámetro replacer de stringify
// Usa el sustituto para filtrar como una matriz localStorage.setItem('zc', JSON.stringify(obj, ['a','e' , 'f']))
// {"a":1,"e":5,"f":6}
console.log(localStorage.getItem('zc')) Cuando replacer es una matriz, podemos simplemente filtrar los atributos que necesitamos, lo cual es un buen truco.
Usar JSON.parse(JSON.stringify) es una de las formas más simples y violentas de implementar copias profundas de objetos. Pero como dice el título, utilizar este método de copia profunda requiere una consideración cuidadosa.
Problema de referencia circular, stringify informará
la función de error, undefined , se ignorará Symbol ,
NaN , Infinity y -Infinity se serializarán en null
...
Por lo tanto, cuando utilice JSON.parse(JSON.stringify) para realizar una copia profunda, debe Piensa con cuidado. Si no existen los peligros ocultos mencionados anteriormente, JSON.parse(JSON.stringify) es una solución de copia profunda factible.
Cuando programamos con matrices, a menudo utilizamos la función map . Con el parámetro replacer , podemos usar este parámetro para implementar la función map del objeto.
const Mapa de objetos = (obj, fn) => {
if (tipo de fn! == "función") {
throw new TypeError(`${fn} no es una función!`);
}
// Primero llame a JSON.stringify(obj, replacer) para implementar la función de mapa // Luego llame a JSON.parse para volver a convertirlo en un objeto return JSON.parse(JSON.stringify(obj, fn));
};
// Por ejemplo, lo siguiente multiplica el valor del atributo del objeto obj por 2
objeto constante = {
un: 1,
segundo: 2,
c: 3
}
console.log(ObjectMap(obj, (clave, val) => {
if (tipo de valor === "número") {
valor de retorno * 2;
}
valor de retorno;
})) Muchos estudiantes pueden preguntarse por qué se requiere un juicio adicional. ¿No es posible simplemente return value * 2 ?
Como se mencionó anteriormente, replacer pasa primero el objeto que se va a serializar. Se ignora el objeto * 2 => NaN => toJSON(NaN) => undefinido => y no habrá análisis de pares clave-valor posteriores.
Con la función replacer , también podemos eliminar ciertos atributos del objeto.
objeto constante = {
nombre: 'zcxiaobao',
edad: 18
}
// {"edad":18}
JSON.stringify(obj, (clave, val) => {
// Cuando el valor de retorno no está definido, esta propiedad se ignorará if (key === 'name') {
devolver indefinido;
}
valor de retorno;
}) JSON.stringify puede serializar objetos en cadenas, por lo que podemos usar métodos de cadena para implementar juicios simples de igualdad de objetos.
//Determinar si la matriz contiene un objeto const nombres = [
{nombre:'zcxiaobao'},
{nombre:'txtx'},
{nombre:'mimi'},
];
const zcxiaobao = {nombre:'zcxiaobao'};
// verdadero
JSON.stringify(nombres).incluye(JSON.stringify(zcxiaobao))
// Determinar si los objetos son iguales const d1 = {type: 'div'}
const d2 = {tipo: 'div'}
// verdadero
JSON.stringify(d1) === JSON.stringify(d2); Con la ayuda de las ideas anteriores, también podemos lograr una deduplicación de objetos de matriz simple.
Pero dado que los resultados de la serialización JSON.stringify {x:1, y:1} y {y:1, x:1} son diferentes, necesitamos procesar los objetos en la matriz antes de comenzar.
Método 1: organizar las claves de cada objeto en la matriz en el orden del diccionario
arr.forEach(item => {
constante nuevoItem = {};
Object.keys(item) // Obtener clave de objeto value.sort() // Valor clave sorting.map(key => { // Generar nuevo objeto newItem[key] = item[key];
})
// Utilice newItem para realizar la operación de deduplicación}) Pero el método uno es un poco engorroso. JSON.stringify proporciona replacer , que puede filtrar la matriz.
Método 2: replacer
la función de formato de matriz de reemplazo única (arr) {
const keySet = nuevo conjunto();
const objeto único = {}
// Extrae todas las claves arr.forEach(item => {
Object.keys(elemento).forEach(clave => keySet.add(clave))
})
sustituto constante = [...keySet];
arr.forEach(elemento => {
// Todos los objetos se filtran según el reemplazo del valor clave especificado. Unique[JSON.stringify(item, replacer)] = item;
})
devolver Object.keys(único).map(u => JSON.parse(u))
}
//Prueba única([{}, {},
{x:1},
{x:1},
{a:1},
{x:1,a:1},
{x:1,a:1},
{x:1,a:1,b:1}
])
// Devuelve el resultado [{},{"x":1},{"a":1},{"x":1,"a":1},{"x":1,"a":1 ,"b":1}]