Интервьюер: Что не так с реализацией глубокого копирования с помощью JSON.stringify()

опрос

Зачем глубокое копирование

Переменные в JS хранятся в памяти в типах значений и ссылочных типах:
Тип значения:
1. Занятое пространство фиксируется и хранится в стеке;
2. Сохраняется и копируется само значение;
3. Базовый тип данных — это тип значения (String, Number, undefined, Boolean, Null);
Тип ссылки:
1. Занятое пространство не фиксируется и хранится в куче;
2. Сохраняется и копируется указатель на объект;
3. Объект, построенный с помощью метода new(), является ссылочным типом;

Таким образом, сложные объекты, такие как Object и Array, необходимо глубоко копировать;

Что не так с глубокой копией JSON.stringify()?

Самый простой способ сделать глубокую копию — использовать JSON.stringify(), сделать глубокую копию, масштабировать:

var obj={
	name:'大雄',
    age:21
};
var obj1=JSON.parse(JSON.stringify(obj));

Таким образом, с глубоким копированием проблем нет; если у нас есть следующие объекты, как насчет глубокого копирования?

var obj1={
	name:'大雄',
    say:function(){
		console.log('我会说话哦!');
    },
    birthday:new Date('1990/01/01')
};
var obj2=JSON.parse(JSON.stringify(obj));
console.log(obj2);
// {name: "大雄", birthday: "1989-12-31T16:00:00.000Z"}

Мы видели, что когда в наших объектах есть функции и типы дат, типы дат преобразуются в строки; атрибуты функций просто исчезают! Это большая проблема! После нашего теста мы обнаружили:

  1. неопределенные, произвольные функции и значения символов игнорируются при сериализации
  2. Date вызывает toJSON(), чтобы преобразовать его в строку (Date.toISOString()), поэтому он будет рассматриваться как строка.
  3. Числовые значения в форматах NaN и Infinity и null обрабатываются как null.
  4. Другие типы объектов, включая Map/Set/WeakMap/WeakSet, будут сериализовать только перечисляемые свойства.
  5. Выполнение этого метода для объектов, содержащих циклические ссылки (объекты, ссылающиеся друг на друга, образующие бесконечный цикл), вызовет ошибку.
// 下面就是循环引用;
var obj1 = {
  x: 1, 
  y: 2
};
obj1.z = obj1;
var obj2 = JSON.parse(JSON.stringify(obj1)); // 栈溢出,抛出错误;

Поэтому, когда в объекте, который мы хотим клонировать, есть ссылочные типы, мы можем проходить его только рекурсивно, и мы не будем здесь его расширять.

Сколько параметров имеет JSON.stringify()?

JSON.stringify(value[, replacer [, space]])

Мы в основном смотрим, для чего используются второй параметр и третий параметр.

заменитель необязательный

  1. Если параметр является функцией, во время сериализации каждый атрибут сериализованного значения преобразуется и обрабатывается функцией;
  2. Если параметр является массивом, только имена свойств, содержащиеся в этом массиве, будут сериализованы в окончательную строку JSON;
  3. Если этот параметр имеет значение null или не указан, все свойства объекта будут сериализованы;
var obj1={
	x:1,
    y:2
};
var obj2=JOSN.stringify(obj1,function(key,value){
	if(typeof value == 'number'){
		return value*2
	}
	return value;
});
// "{x:2,y:4}"

Давайте еще раз посмотрим на последний параметр:

пробел необязательный

1. Укажите пустую строку для отступа, чтобы украсить вывод (красивая печать)
2. Если параметр является числом, он представляет количество пробелов;

Например

JSON.stringify(obj1,null,'\t');
"{
	"x": 1,
	"y": 2
}"

I мы используем вкладки для отступов;

Оригинальность не так проста, пожалуйста, ставьте лайк и подписывайтесь, большое спасибо!