Цель: Добиться копирования объекта в глубину.
Примечания:
- Копирование работает с:
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