Цель: Добиться копирования объекта в глубину.
Примечания:
- Копирование работает с:
undefined,null,number,bigint,string,boolean,array,object. - Копирование не работает с:
symbol,function. Они переносятся в клонируемый объект как есть (это из-за того как устроены внутри). - Копирование поддерживает другие сложные объекты, но реализация лежит на ваших плечах (внизу есть пример как это сделать).
- Поддержка тех типов что не предоставлена, будут пропущены, ключи для них созданы не будут, но есть специальный флаг, который позволит сделать попытку скопировать свойство.
- Копирование работает со всеми типами свойств, дескриптор каждого свойства учитывается.
Опции:
{
prototype: true/false/null, /// копирует ссылку на прототип в копируемый объект, null - установка прототипа в значение null
compatibleType: true/false, /// данная опция делает попытку скопировать объект, тип которого не поддерживается, если попытка неудачна свойство не будет создано
descriptorType: "both"/"data"/"accessor", /// от указанного типа зависит копирование свойств конкретного типа
descriptorProps: {
/// если указывается false то это означает что свойство с данным дескриптором и со значением false не учитывается, а значит не копируется;
/// если указывается true, тогда не играет роли какое значение имеет свойство true или false - оно будет скопировано
writable: true/false,
enumerable: true/false,
configurable: true/false,
},
supplementalTypes: {} /// в объекте указываются специальные методы, которые реализуют специальные объекты
}Советую поэкспериментировать чтобы четко понять как работают данные флаги.
Как добавить поддержку специального типа (на примере Set, Map и Date):
Нужно передать во второй аргумент объект со свойством supplementalTypes где свойства это имена типов данных, например для типа Set, свойство set, для Map свойство map, для Date свойство date итд. Функция, которая устанавливается такому свойству может быть определена как угодно. Параметры у функции: первый это объект, который клонируется, второй это клонируемая функция из алгоритма.
const cloneObject = {
set: function (object, cloneF) {
let result = new Set();
for (let data of object) {
if (typeof data === "object" && data !== null) result.add(cloneF(data));
else result.add(data);
}
return result;
},
map: function (object, cloneF) {
let result = new Map();
for (let [key, value] of object) {
if (typeof value === "object" && value !== null) result.set(key, cloneF(value));
else result.set(key, value);
}
return result;
},
date: function (object, cloneF) {
return new Date(object.getTime());
},
};Тест (не забываем взять из исходника выше реализованные типы данных):
let o1 = {
name: "Maxim",
data: {
key1: "62s34i8g72s",
key2: "82s3438g72s",
key3: "72s34m8g72s",
},
list: [1, 2, 3, 4, 5, new Set(["look", "at", "that"])],
specialList: new Set(["one", "two", "three"]),
date: new Date(),
map: new Map([[{id:1},"water"],[{id:2},"fire"],[{id:3},"air"]])
};
/// Накидываем циклические ссылки
o1.data.toListCycle = o1.list;
o1.list.toDataCycle = o1.data;
o1.toItself = o1;
/// Клонируем
let cloned = deepClone(o1, {supplementalTypes: cloneObject});
/// Смотрим на результат, можно конечно вручную в консоли потрогать для убедительности
console.log(o1.list[5] === cloned.list[5]); /// expected: false