JSON.stringify
é um método frequentemente usado no desenvolvimento diário. Você pode realmente usá-lo com flexibilidade?
Antes de estudar este artigo, Xiaobao deseja que todos respondam a algumas perguntas e aprendam stringify
em profundidade.
stringify
possui vários parâmetros. Qual é a utilidade de cada parâmetro?stringify
null、undefined、NaN
?ES6
Haverá tratamento especial durante o processo de serialização do tipo Symbol
e BigInt
stringify
stringify
não é adequado para cópia profunda? pode deixar uma impressão primeiro.
Na programação diária, costumamos usar o método JSON.stringify
para converter um objeto em um formato de string JSON
.
const stu = { nome: 'zcxiaobao', idade: 18 } // {"nome":"zcxiaobao","idade":18} console.log(JSON.stringify(stu));
Mas stringify
é realmente tão simples? Vamos primeiro dar uma olhada na definição de stringify
no MDN
.
Estados MDN: O método JSON.stringify()
converte um objeto ou valor JavaScript
em JSON
. Se uma função replacer
for especificada, o valor pode ser opcionalmente substituído ou o replacer
especificado é uma matriz. .
Depois de ler a definição, Xiaobao ficou surpreso. stringfy
tem mais de um parâmetro? Claro, stringify
tem três parâmetros.
Vamos dar uma olhada na sintaxe stringify
e na introdução dos parâmetros:
JSON.stringify(value[, replacer [, space]])
value
: O valor a ser sequenciado em uma string JSON.replacer
(opcional) Se o parâmetro for uma função , durante o processo de serialização, cada atributo do valor serializado será convertido e processado pela função;
se o parâmetro for um array , apenas as propriedades contidas neste array serão Somente o atributo; os nomes serão serializados na string JSON
final.
Se este parâmetro for null
ou não fornecido, todos os atributos do objeto serão serializados.
space
(opcional): especifica a string de espaço em branco usada para recuo, usada para embelezar a saída. Se o parâmetro for um número, ele representa o número de espaços. O limite superior é 10.
Se o valor for menor que 1, significa que não há espaços.
Se o parâmetro for uma string (quando o comprimento da string exceder 10 letras, as primeiras 10 letras serão consideradas), a string será tratada como espaços
. o parâmetro não é fornecido (ou é nulo), não haverá espaços
replacer
replacer
como função
replacer
como função, possui dois parâmetros, chave ( key
) e valor ( value
), e ambos os parâmetros serão serializados.
No início, a função de substituição será passada em uma string vazia como valor-chave, representando o objeto a ser stringificado . É importante entender isso. A função replacer
não analisa o objeto em pares de valores-chave quando ele surge, mas primeiro passa o objeto a ser serializado . Em seguida, as propriedades de cada objeto ou array são passadas sequencialmente. Se o valor de retorno da função for indefinido ou função, o valor do atributo será filtrado e o restante seguirá as regras de retorno.
//repalcer aceita valor-chave de dois parâmetros // valor-chave é cada par de valores-chave do objeto // então podemos simplesmente filtrar com base no tipo de chave ou valor function replacer(key, value) { if (tipo de valor === "string") { retornar indefinido; } valor de retorno; } // a função pode testar a função sozinha replacerFunc(key, value) { if (tipo de valor === "string") { retornar() => {}; } valor de retorno; } const foo = {fundação: "Mozilla", modelo: "caixa", semana: 45, transporte: "carro", mês: 7}; const jsonString = JSON.stringify(foo, replacer);
JSON
é {"week":45,"month":7}
mas se a serialização for uma matriz, se replacer
retornar undefined
ou função, o valor atual não será ignorado e será substituído por null
.
lista const = [1, '22', 3] const jsonString = JSON.stringify(list, replacer)
O resultado da serialização JSON
é '[1,null,3]'
replacer
é mais fácil de entender como um array, filtrando os valores-chave que aparecem no array.
const foo = {fundação: "Mozilla", modelo: "caixa", semana: 45, transporte: "carro", mês: 7}; const jsonString = JSON.stringify(foo, ['week', 'month']);
O resultado da serialização JSON é {"week":45,"month":7}
, e apenas week
e month
são retido.
undefined
undefined
e valores Symbol
serão ignorados durante o processo de serialização.
e os valores Symbol
serão ignorados. Quando convertido
apenas para nulo: indefinido será retornado
// 1. A existência desses três valores no valor do atributo do objeto será ignorada const obj = { nome: 'zc', idade: 18, // A função será ignorada sayHello() { console.log('olá mundo') }, // indefinido será ignorado esposa: indefinido, // O valor do símbolo será ignorado id: Symbol(111), // [Símbolo('zc')]: 'zc', } // Resultado de saída: {"name":"zc","age":18} console.log(JSON.stringify(obj)); // 2. Esses três valores no array serão convertidos em nulos lista constante = [ 'zc', 18, //Função convertida para nula function digaOlá() { console.log('olá mundo') }, // indefinido convertido em nulo indefinido, //Símbolo convertido para nulo Símbolo(111) ] // ["zc",18,nulo,nulo,nulo] console.log(JSON.stringify(lista)) // 3. A conversão separada desses três valores retornará indefinido console.log(JSON.stringify(indefinido)) // indefinido console.log(JSON.stringify(Symbol(111))) // indefinido console.log(JSON.stringify(função dizerOlá() { console.log('olá mundo') })) //
converte o valor Se houver toJSON()
(), qualquer valor toJSON()
retornar será o valor retornado pelo resultado da serialização, e os outros valores serão. ignorado.
const obj={ nome: 'zc', paraJSON(){ retornar 'retornar para JSON' } } //volta para JSON console.log(JSON.stringify(obj));
valores booleanos, números e strings será automaticamente convertido no valor original JSON durante o processo de serialização.
.stringify([novo Número(1), new String("zcxiaobao"), novo Boolean(true)]); // [1,"zcxiaobao",true]
O recurso quatro visa principalmente valores especiais em JavaScript
, como NaN
, Infinity
e null no tipo Number
. Esses três tipos de valores serão tratados como null
durante a serialização .
// [nulo,nulo,nulo,nulo,nulo] JSON.stringify([nulo, NaN, -NaN, Infinito, -Infinity]) // O recurso 3 mencionou que os objetos de empacotamento de valores booleanos, números e strings serão automaticamente convertidos nos valores originais correspondentes durante o processo de serialização // A conversão de tipo implícita chamará a classe de empacotamento, então Number => NaN será chamado primeiro // Depois converte para nulo // 1/0 => Infinito => nulo JSON.stringify([Number('123a'), +'123a', 1/0])
O método toJSON
(igual a Date.toISOString()
) é implantado no objeto Date
para convertê-lo em um string, então JSON.stringify() serializará o valor Date em uma string de formato de hora .
// "2022-03-06T08:24:56.138Z" JSON.stringify(new Date())
Ao mencionar o recurso Símbolo, quando Symbol
é usado como um valor, objetos, matrizes e usos individuais serão ignorados, convertidos em null
e convertidos em undefined
respectivamente.
Da mesma forma, todas as propriedades com Símbolo como chave de propriedade serão completamente ignoradas, mesmo que sejam forçadas a serem incluídas no parâmetro substituto .
const obj={ nome: 'zcxiaobao', idade: 18, [Símbolo('lyl')]: 'único' } função substituto(chave, valor) { if (typeof chave === 'símbolo') { valor de retorno; } } // indefinido JSON.stringify(obj, replacer);
No caso acima, podemos ver que, embora especifiquemos à força o valor do tipo Symbol
de retorno por meio de replacer
, ele eventualmente será ignorado.
JSON.stringify
estipula: Tentar converter um valor do tipo BigInt
gerará TypeError
const bigNumber = BigInt(1) // TypeError não capturado: não sei como serializar um BigInt Console.log(JSON.stringify(bigNumber))
O recurso 8 aponta: Executar este método em objetos contendo referências circulares (objetos referem-se uns aos outros, formando um loop infinito) gerará um erro
. A maneira mais simples e violenta é usar JSON.parse(JSON.stringify(obj))
, mas a cópia profunda nesse método tem grandes armadilhas. O principal problema é que stringify
não consegue lidar com o problema de referência circular.
const obj={ nome: 'zcxiaobao', idade: 18, } const loopObj = { obj } // Forme uma referência circular obj.loopObj = loopObj; JSON.stringify(obj) /* TypeError não capturado: convertendo estrutura circular em JSON -> começando no objeto com o construtor 'Object' | propriedade 'loopObj' -> objeto com construtor 'Object' --- propriedade 'obj' fecha o círculo em JSON.stringify (<anônimo>) em <anônimo>:10:6 */
a serialização de propriedades enumeráveis para objetos (incluindo Map/Set/WeakMap/WeakSet
), além de algumas das situações mencionadas acima, stringify
também estipula claramente que apenas propriedades enumeráveis serão serializadas
// Não enumerável propriedades serão ignoradas por padrão // {"age":18} JSON.stringify( Objeto.criar( nulo, { nome: {valor: 'zcxiaobao', enumerável: falso}, idade: {valor: 18, enumerável: verdadeiro} } ) );
O objeto localStorage
é usado para salvar os dados de todo o site por um longo tempo. Os dados salvos não têm prazo de validade até serem excluídos manualmente. Normalmente armazenamos na forma de objetos.
Basta chamar o método do objeto localStorage
const obj = { nome: 'zcxiaobao', idade: 18 } //Basta chamar localStorage.setItem() localStorage.setItem('zc', obj); //O resultado final do retorno é [object Object] // Pode-se observar que a simples chamada de localStorage falha console.log(localStorage.getItem('zc'))
localStorage
coopera com JSON.stringify
localStorage.setItem('zc', JSON.stringify(obj)); //O resultado final do retorno é {nome: 'zcxiaobao', idade: 18} Console.log(JSON.parse(localStorage.getItem('zc')))
assume tal cenário. O backend retorna um objeto longo com muitos atributos, e precisamos apenas de alguns deles e precisamos armazená-los. atributos em localStorage
.
Opção 1: Desestruturação de atribuição + stringify
// Precisamos apenas dos atributos a, e, f const obj = { a:1, b:2, c:3, d:4, e:5, f:6, g:7 } // Desestruturando atribuição const {a,e,f} = obj; // Armazena em localStorage localStorage.setItem('zc', JSON.stringify({a,e,f})) // {"a":1,"e":5,"f":6} console.log(localStorage.getItem('zc'))
usa o parâmetro replacer
de stringify
// Use o substituto para filtrar como uma matriz localStorage.setItem('zc', JSON.stringify(obj, ['a','e' , 'f'])) // {"a":1,"e":5,"f":6} console.log(localStorage.getItem('zc'))
Quando replacer
é um array, podemos simplesmente filtrar os atributos que precisamos, o que é um bom truque.
Usar JSON.parse(JSON.stringify)
é uma das maneiras mais simples e violentas de implementar cópias profundas de objetos. Mas, como diz o título, usar esse método de cópia profunda requer uma consideração cuidadosa.
Problema de referência circular, stringify
reportará
função de erro, undefined
, Symbol
será ignorado,
NaN
, Infinity
e -Infinity
serão serializados em null
...
Portanto, ao usar JSON.parse(JSON.stringify)
para fazer cópia profunda, você deve pense com cuidado. Se não houver perigos ocultos mencionados acima, JSON.parse(JSON.stringify)
é uma solução viável de cópia profunda.
Ao programar com arrays, geralmente usamos a função map
. Com o parâmetro replacer
, podemos usar este parâmetro para implementar a função map
do objeto.
const ObjectMap = (obj, fn) => { if (typeof fn! == "função") { throw new TypeError(`${fn} não é uma função!`); } // Primeiro chame JSON.stringify(obj, replacer) para implementar a função de mapa // Em seguida, chame JSON.parse para reconvertê-lo em um objeto return JSON.parse(JSON.stringify(obj, fn)); }; // Por exemplo, o seguinte multiplica o valor do atributo do objeto obj por 2 const obj={ um: 1, b: 2, c: 3 } console.log(ObjectMap(obj, (chave, val) => { if (tipo de valor === "número") { valor de retorno * 2; } valor de retorno; }))
Muitos estudantes podem estar se perguntando por que é necessário um julgamento adicional. Não é possível return value * 2
?
Conforme mencionado acima, replacer
passa primeiro o objeto a ser serializado. O objeto * 2 => NaN => toJSON(NaN) => undefined => é ignorado e não haverá análise subsequente de pares de valores-chave.
Com a função replacer
, também podemos excluir certos atributos do objeto.
const obj={ nome: 'zcxiaobao', idade: 18 } // {"idade":18} JSON.stringify(obj, (chave, val) => { // Quando o valor de retorno for indefinido, esta propriedade será ignorada if (key === 'name') { retornar indefinido; } valor de retorno; })
JSON.stringify
pode serializar objetos em strings, para que possamos usar métodos de string para implementar julgamentos simples de igualdade de objetos.
//Determina se o array contém um objeto constnames = [ {nome:'zcxiaobao'}, {nome:'txtx'}, {nome:'meu'}, ]; const zcxiaobao = {nome:'zcxiaobao'}; // verdadeiro JSON.stringify(nomes).inclui(JSON.stringify(zcxiaobao)) // Determina se os objetos são iguais const d1 = {type: 'div'} const d2 = {tipo: 'div'} // verdadeiro JSON.stringify(d1) === JSON.stringify(d2);
Com a ajuda das ideias acima, também podemos obter desduplicação simples de objeto array.
Mas como os resultados da serialização JSON.stringify
{x:1, y:1}
e {y:1, x:1}
são diferentes, precisamos processar os objetos no array antes de começar.
Método 1: organize as chaves de cada objeto na matriz na ordem do dicionário
arr.forEach(item => { const novoItem = {}; Object.keys(item) // Obtém a chave do objeto value.sort() // Valor da chave sorting.map(key => { // Gera novo objeto newItem[key] = item[key]; }) // Use newItem para realizar a operação de desduplicação})
Mas o método um é um pouco complicado. JSON.stringify
fornece replacer
, que pode filtrar o array.
Método 2: replacer
a função de formato de matriz de substituição unique(arr) { const keySet=new Set(); const únicoObj = {} // Extrai todas as chaves arr.forEach(item => { Object.keys(item).forEach(chave => keySet.add(chave)) }) const substituto = [...keySet]; arr.forEach(item => { // Todos os objetos são filtrados de acordo com o valor-chave especificado replacer unique[JSON.stringify(item, replacer)] = item; }) retornar Object.keys(unique).map(u => JSON.parse(u)) } //Teste único([{}, {}, {x:1}, {x:1}, {a:1}, {x:1,a:1}, {x:1,a:1}, {x:1,a:1,b:1} ]) // Retorna o resultado [{},{"x":1},{"a":1},{"x":1,"a":1},{"x":1,"a":1 ,"b":1}]