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}]