
JSON.stringify — метод, часто используемый в повседневной разработке. Можете ли вы действительно использовать его гибко?
Прежде чем приступить к изучению этой статьи, Сяобао предлагает всем ответить на несколько вопросов и подробно изучить stringify .
stringify имеет несколько параметров. Каково использование каждого параметра?stringify null、undefined、NaN ?ES6 Будет ли применяться специальная обработка типов Symbol и BigInt ?stringify не подходит для глубокого копирования?stringify ?Контекст всей статьи соответствует приведенной ниже карте связей. может произвести первое впечатление.

В повседневном программировании мы часто используем метод JSON.stringify для преобразования объекта в строковую форму JSON .
константа сту = {
имя: 'zcxiaobao',
возраст: 18
}
// {"name":"zcxiaobao","age":18}
console.log(JSON.stringify(stu)); Но действительно ли stringify так прост? Давайте сначала посмотрим на определение stringify в MDN .
MDN утверждает: метод JSON.stringify() преобразует объект или значение JavaScript в строку JSON . Если указана функция replacer , значение может быть заменено по желанию, или указанный replacer является массивом. Содержит свойства, указанные в массиве. .
Прочитав определение, Сяобао удивился. Имеет ли stringfy более одного параметра? Конечно, stringify имеет три параметра.
Давайте посмотрим на синтаксис stringify и введение в параметры:
JSON.stringify(value[, replacer [, space]])
value : значение, которое нужно упорядочить в строку JSON.replacer (необязательно). Если параметр является функцией , во время процесса сериализации каждый атрибут сериализованного значения будет преобразован и обработан функцией.
Если параметр является массивом , только свойства, содержащиеся в этом массиве, будут только атрибутом; имена будут сериализованы в окончательную строку JSON .
Если этот параметр имеет null или не указан, все атрибуты объекта будут сериализованы.
space (необязательно): указывает строку пробелов, используемую для отступа, используемую для украшения вывода. Если параметр является числом, он представляет количество пробелов. Верхний предел — 10.
Если значение меньше 1, это означает, что пробелов нет.
Если параметр представляет собой строку (когда длина строки превышает 10 букв, берутся первые 10 букв), строка будет рассматриваться как пробелы
. параметр не указан (или имеет значение null), пробелов не будет
. Попробуем использовать replacer .
replacer как функция
replacer как функция, он имеет два параметра: ключ ( key ) и значение ( value ), и оба параметра будут сериализованы.
Вначале функция replacer будет передана в пустой строке в качестве значения ключа, представляющего объект, который необходимо преобразовать в строку . Важно это понимать. Функция replacer не разбирает объект на пары ключ-значение, когда он появляется, а сначала передает объект для сериализации . Затем свойства каждого объекта или массива передаются последовательно. Если возвращаемое значение функции не определено или является функцией, значение атрибута будет отфильтровано , а остальное будет следовать правилам возврата.
// repalcer принимает значение ключа двух параметров
// значением ключа является каждая пара ключ-значение объекта, // поэтому мы можем просто фильтровать по типу ключа или значения function replacer(key, value) {
if (typeof value === "строка") {
вернуть неопределенное значение;
}
возвращаемое значение;
}
// функция может самостоятельно проверять функцию replacerFunc(key, value) {
if (typeof value === "строка") {
возврат () => {};
}
возвращаемое значение;
}
const foo = {основание: "Mozilla", модель: "коробка", неделя: 45, транспорт: "автомобиль", месяц: 7};
const jsonString = JSON.stringify(foo, replacer); Результатом сериализации JSON является {"week":45,"month":7}
но если сериализация представляет собой массив, если функция replacer возвращает undefined или функцию, текущее значение не будет игнорироваться и будет заменено на null .
константный список = [1, '22', 3] const jsonString = JSON.stringify(list, replacer)
Результатом сериализации JSON является '[1,null,3]'.
replacer
легче понять как массив, фильтруя ключевые значения, появляющиеся в массиве.
const foo = {основание: "Mozilla", модель: "коробка", неделя: 45, транспорт: "автомобиль", месяц: 7};
const jsonString = JSON.stringify(foo, ['week', 'month']); Результат сериализации JSON — {"week":45,"month":7} , и только значения атрибутов week и month являются сохранено.
появляющиеся в значениях атрибутов объектов, не являющихся массивами: undefined , любая функция и значения Symbol будут игнорироваться в процессе сериализации.
Появляющиеся в массивах: undefined , любая функция, и Значения Symbol будут игнорироваться. При преобразовании
только в нуль: будет возвращено неопределенное
// 1. Существование этих трех значений в значении атрибута объекта будет игнорироваться const obj = {
имя: 'зк',
возраст: 18,
// Функция будет игнорироваться SayHello() {
console.log('Привет, мир')
},
// неопределенное будет игнорироваться жена: неопределенный,
// Значение символа будет игнорироваться id: Символ(111),
// [Символ('zc')]: 'zc',
}
// Результат вывода: {"name":"zc","age":18}
console.log(JSON.stringify(obj));
// 2. Эти три значения в массиве будут преобразованы в ноль
константный список = [
'зк',
18,
// Функция преобразуется в ноль
функция SayHello() {
console.log('Привет, мир')
},
// неопределенное преобразование в ноль
неопределенный,
// Символ конвертируется в ноль
Символ(111)
]
// ["zc",18,ноль,ноль,ноль]
console.log(JSON.stringify(список))
// 3. Отдельное преобразование этих трех значений вернет неопределенное значение
console.log(JSON.stringify(undefined)) // не определено
console.log(JSON.stringify(Symbol(111))) // не определено
console.log(JSON.stringify(functionsayHello() {
console.log('Привет, мир')
})) // неопределенная преобразует значение. Если существует метод toJSON() , какое бы значение ни возвращало метод toJSON() оно будет тем значением, которое возвращает результат сериализации, а остальные значения будут такими же. игнорируется.
константный объект = {
имя: 'зк',
toJSON(){
return 'вернуться в JSON'
}
}
// возвращаемся в JSON
console.log(JSON.stringify(obj)); логических значений, чисел и строк будет автоматически преобразована в соответствующее исходное значение JSON в процессе сериализации.
. stringify([new Number(1), new String("zcxiaobao"), new Boolean(true)]);
// [1,"zcxiaobao",true] Четвертая функция в основном нацелена на специальные значения в JavaScript , такие как NaN , Infinity и null в типе Number . Эти три типа значений во время сериализации будут обрабатываться как null .
// [ноль, ноль, ноль, ноль, ноль]
JSON.stringify([null, NaN, -NaN, бесконечность, -бесконечность])
// В функции 3 упоминалось, что объекты упаковки логических значений, чисел и строк будут автоматически преобразованы в соответствующие исходные значения в процессе сериализации // Неявное преобразование типов вызовет класс упаковки, поэтому Number => NaN будет позвонил первым
// Затем преобразуем в ноль
// 1/0 => бесконечность => ноль
JSON.stringify([Number('123a'), +'123a', 1/0]) Метод toJSON (тот же, что и Date.toISOString() ) развертывается в объекте Date для преобразования его в строка, поэтому JSON.stringify() сериализует значение даты в строку формата времени .
// "2022-03-06T08:24:56.138Z" JSON.stringify(new Date())
При упоминании функции символа, когда тип Symbol используется в качестве значения, объекты, массивы и отдельные варианты использования будут игнорироваться, конвертироваться в null и конвертироваться в undefined соответственно.
Аналогично, все свойства с символом в качестве ключа свойства будут полностью игнорироваться, даже если их принудительно включить в параметр replacer .
константный объект = {
имя: 'zcxiaobao',
возраст: 18,
[Символ('lyl')]: 'уникальный'
}
функция replacer(ключ, значение) {
if (typeof key === 'symbol') {
возвращаемое значение;
}
}
// неопределенный
JSON.stringify(obj, replacer); Из приведенного выше случая мы видим, что, хотя мы принудительно указываем возвращаемое значение типа Symbol через replacer , в конечном итоге оно будет проигнорировано.
JSON.stringify предусматривает: Попытка преобразовать значение типа BigInt приведет к возникновению TypeError
const bigNumber = BigInt(1). // Неперехваченная ошибка типа: не знаю, как сериализовать BigInt Console.log(JSON.stringify(bigNumber))
Функция 8 указывает: выполнение этого метода для объектов, содержащих циклические ссылки (объекты ссылаются друг на друга, образуя бесконечный цикл), приведет к ошибке
. Самый простой и жестокий способ — использовать JSON.parse(JSON.stringify(obj)) , но глубокое копирование с помощью этого метода имеет огромные подводные камни. Ключевая проблема заключается в том, что stringify не может справиться с проблемой циклических ссылок.
константный объект = {
имя: 'zcxiaobao',
возраст: 18,
}
константный циклObj = {
объект
}
// Формируем циклическую ссылку obj.loopObj =loopObj;
JSON.stringify(объект)
/* Неперехваченная ошибка типа: преобразование циклической структуры в JSON
--> начиная с объекта с помощью конструктора Object
| свойство «loopObj» -> объект с конструктором «Объект»
--- свойство 'obj' замыкает круг
в JSON.stringify (<анонимно>)
в <анонимно>:10:6
*/ сериализации перечислимых свойств объектов (включая Map/Set/WeakMap/WeakSet ), в дополнение к некоторым ситуациям, упомянутым выше, stringify также четко оговаривает, что сериализоваться будут только перечисляемые свойства
// Неперечислимые свойства будут игнорироваться по умолчанию // {"age":18}
JSON.stringify(
Object.create(
нулевой,
{
имя: {значение: 'zcxiaobao', перечисляемое: false},
возраст: {значение: 18, перечисляемое: правда}
}
)
); Объект localStorage используется для длительного хранения данных всего веб-сайта. Сохраненные данные не имеют срока действия, пока они не будут удалены вручную. Обычно мы храним его в виде объектов.
Просто вызовите метод объекта localStorage
const obj = {
имя: 'zcxiaobao',
возраст: 18
}
// Просто вызовите localStorage.setItem()
localStorage.setItem('zc', obj);
//Окончательный результат возврата: [объект Object]
// Видно, что простой вызов localStorage не удался console.log(localStorage.getItem('zc')) localStorage взаимодействует с методом JSON.stringify
localStorage.setItem('zc', JSON.stringify(obj));
//Окончательный результат возврата: {name: 'zcxiaobao', age: 18}
Console.log(JSON.parse(localStorage.getItem('zc'))) предполагает такой сценарий. Серверная часть возвращает длинный объект с множеством атрибутов, а нам нужно только несколько из них, и нам нужно их сохранить. атрибуты в localStorage .
Вариант 1: Деструктуризация присваивания + stringify
// Нам нужны только атрибуты a, e, f const obj = {
а:1, б:2, в:3, г:4, д:5, е:6, ж:7
}
// Деструктуризация присваивания const {a,e,f} = obj;
// Сохраняем в localStorage
localStorage.setItem('zc', JSON.stringify({a,e,f}))
// {"a":1, "e":5, "f":6}
console.log(localStorage.getItem('zc')) использует параметр replacer stringify
// Используйте replacer для фильтрации в виде массива localStorage.setItem('zc', JSON.stringify(obj, ['a','e' , 'ф']))
// {"a":1, "e":5, "f":6}
console.log(localStorage.getItem('zc')) Когда replacer представляет собой массив, мы можем просто отфильтровать нужные нам атрибуты, что является небольшой хорошей хитростью.
Использование JSON.parse(JSON.stringify) — один из самых простых и жестоких способов реализации глубокого копирования объектов. Но, как следует из названия, использование этого метода глубокого копирования требует тщательного рассмотрения.
Проблема с циклической ссылкой, stringify сообщит об ошибке
, undefined , Symbol будет проигнорирован,
NaN , Infinity и -Infinity будут сериализованы в null
...
Поэтому при использовании JSON.parse(JSON.stringify) для глубокого копирования вы должны подумайте хорошенько. Если вышеупомянутых скрытых опасностей нет, JSON.parse(JSON.stringify) является подходящим решением для глубокого копирования.
При программировании с массивами мы часто используем функцию map . С помощью параметра replacer мы можем использовать этот параметр для реализации функции map объекта.
const ObjectMap = (obj, fn) => {
if (typeof fn !== "функция") {
throw new TypeError(`${fn} не является функцией!`);
}
// Сначала вызовите JSON.stringify(obj, replacer) для реализации функции карты // Затем вызовите JSON.parse, чтобы повторно преобразовать ее в объект return JSON.parse(JSON.stringify(obj, fn));
};
// Например, следующее умножает значение атрибута объекта obj на 2
константный объект = {
а: 1,
б: 2,
с: 3
}
console.log(ObjectMap(obj, (key, val) => {
if (typeof value === "число") {
возвращаемое значение * 2;
}
возвращаемое значение;
})) Многие студенты могут задаться вопросом, почему требуется дополнительное суждение. Разве нельзя просто return value * 2 ?
Как упоминалось выше, функция replacer сначала передает объект, подлежащий сериализации. Объект * 2 => NaN => toJSON(NaN) => undefined => игнорируется , и последующий анализ пары ключ-значение не выполняется.
С помощью функции replacer мы также можем удалить определенные атрибуты объекта.
константный объект = {
имя: 'zcxiaobao',
возраст: 18
}
// {"возраст":18}
JSON.stringify(obj, (key, val) => {
// Если возвращаемое значение не определено, это свойство будет игнорироваться if (key === 'name') {
вернуть неопределенное значение;
}
вернуть значение;
}) JSON.stringify может сериализовать объекты в строки, поэтому мы можем использовать строковые методы для реализации простых оценок равенства объектов.
//Определяем, содержит ли массив объект const names = [
{имя:'zcxiaobao'},
{имя:'txtx'},
{имя:'mymy'},
];
const zcxiaobao = {name:'zcxiaobao'};
// истинный
JSON.stringify(имена).includes(JSON.stringify(zcxiaobao))
// Определяем, равны ли объекты const d1 = {type: 'div'}
const d2 = {тип: 'div'}
// истинный
JSON.stringify(d1) === JSON.stringify(d2); . С помощью приведенных выше идей мы также можем добиться простой дедупликации объектов массива.
Но поскольку результаты сериализации JSON.stringify {x:1, y:1} и {y:1, x:1} разные, перед запуском нам необходимо обработать объекты в массиве.
Метод 1. Расположите ключи каждого объекта в массиве в словарном порядке
arr.forEach(item => {
константный новыйItem = {};
Object.keys(item) // Получаем ключ объекта value.sort() // Значение ключа sorting.map(key => { // Генерируем новый объект newItem[key] = item[key];
})
// Используйте newItem для выполнения операции дедупликации}) Но первый метод немного громоздкий. JSON.stringify предоставляет параметр формата массива- replacer , который может фильтровать массив.
Способ 2. replacer
функцию формата массива-заменителя unique(arr) {
const keySet = новый Set();
const uniqueObj = {}
// Извлекаем все ключи arr.forEach(item => {
Object.keys(item).forEach(key => keySet.add(key))
})
const replacer = [...keySet];
arr.forEach(item => {
// Все объекты фильтруются по указанному значению ключа replacer unique[JSON.stringify(item, replacer)] = item;
})
вернуть Object.keys(unique).map(u => JSON.parse(u))
}
//Тест уникальности([{}, {},
{х:1},
{х:1},
{а:1},
{х:1,а:1},
{х:1,а:1},
{х:1,а:1,б:1}
])
// Возвращаем результат [{},{"x":1},{"a":1},{"x":1,"a":1},{"x":1,"a":1 ,"б":1}]