Skip to content

Instantly share code, notes, and snippets.

@dSalieri
Last active October 31, 2023 18:18
Show Gist options
  • Save dSalieri/6755d09257e28f1249e64f9c0ac0e1fe to your computer and use it in GitHub Desktop.
Save dSalieri/6755d09257e28f1249e64f9c0ac0e1fe to your computer and use it in GitHub Desktop.

Revisions

  1. dSalieri revised this gist Jul 15, 2023. 1 changed file with 11 additions and 10 deletions.
    21 changes: 11 additions & 10 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,7 @@ function deepClone(obj, options) {
    },
    };

    const _null = Symbol("null");
    const empty = Symbol("empty");
    const map = new Map();

    return (function clone(obj, who) {
    @@ -27,8 +27,9 @@ function deepClone(obj, options) {
    } else if (map.has(obj)) {
    return map.get(obj);
    }

    let result = options.compatibleType === true ? new Object() : _null;

    const ownKeys = Reflect.ownKeys(obj);
    let result = options.compatibleType === true && ownKeys.length > 0 ? new Object() : empty;

    switch (sortOf(obj)) {
    case "array": {
    @@ -48,16 +49,16 @@ function deepClone(obj, options) {

    map.set(obj, result);

    if (result === _null && options.compatibleType === false) {
    if (result === empty && options.compatibleType === false) {
    if (who === "parent") return null;
    return _null;
    return empty;
    }

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0) {
    Reflect.setPrototypeOf(result, Reflect.getPrototypeOf(obj));
    return result
    };
    if (result !== empty) return result;
    if (who === "child") return empty;
    return null;
    }

    for (let key of ownKeys) {
    const { descriptor, type } = descriptorWithType(obj, key);
    @@ -68,7 +69,7 @@ function deepClone(obj, options) {
    ) continue;

    let cloned = clone(descriptor.value, "child");
    if (cloned !== _null) {
    if (cloned !== empty) {
    if (type === "data" && ["data", "both"].some((v) => v === options.descriptorType)) {
    Reflect.defineProperty(result, key, { ...descriptor, value: cloned });
    } else if (type === "accessor" && ["accessor", "both"].some((v) => v === options.descriptorType)) {
  2. dSalieri revised this gist Jul 15, 2023. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -54,8 +54,10 @@ function deepClone(obj, options) {
    }

    const ownKeys = Reflect.ownKeys(obj);

    if (ownKeys.length === 0) return result;
    if (ownKeys.length === 0) {
    Reflect.setPrototypeOf(result, Reflect.getPrototypeOf(obj));
    return result
    };

    for (let key of ownKeys) {
    const { descriptor, type } = descriptorWithType(obj, key);
    @@ -95,4 +97,4 @@ function deepClone(obj, options) {

    return result;
    }
    }
    }
  3. dSalieri revised this gist Jul 15, 2023. 1 changed file with 2 additions and 4 deletions.
    6 changes: 2 additions & 4 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -54,10 +54,8 @@ function deepClone(obj, options) {
    }

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0) {
    if (who === "parent") return null;
    return _null;
    }

    if (ownKeys.length === 0) return result;

    for (let key of ownKeys) {
    const { descriptor, type } = descriptorWithType(obj, key);
  4. dSalieri revised this gist Jul 10, 2023. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -49,13 +49,13 @@ function deepClone(obj, options) {
    map.set(obj, result);

    if (result === _null && options.compatibleType === false) {
    if (who === "parent") return null
    if (who === "parent") return null;
    return _null;
    }

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0) {
    if(who === "parent") return null;
    if (who === "parent") return null;
    return _null;
    }

    @@ -67,7 +67,7 @@ function deepClone(obj, options) {
    (descriptor.configurable === false && options.descriptorProps.configurable === false)
    ) continue;

    let cloned = clone(Reflect.get(obj, key), "child");
    let cloned = clone(descriptor.value, "child");
    if (cloned !== _null) {
    if (type === "data" && ["data", "both"].some((v) => v === options.descriptorType)) {
    Reflect.defineProperty(result, key, { ...descriptor, value: cloned });
  5. dSalieri revised this gist Feb 26, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion _index.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    **Цель:** Добиться копирования объекта в глубину.

    **Примечания:**
    1. Копирование работает с: _`undefined`_, _`null`_, _`number`_, _`string`_, _`boolean`_, _`array`_, _`object`_.
    1. Копирование работает с: _`undefined`_, _`null`_, _`number`_, _`bigint`_, _`string`_, _`boolean`_, _`array`_, _`object`_.
    2. Копирование не работает с: _`symbol`_, _`function`_. Они переносятся в клонируемый объект как есть (это из-за того как устроены внутри).
    3. Копирование поддерживает другие сложные объекты, но реализация лежит на ваших плечах (внизу есть пример как это сделать).
    4. Поддержка тех типов что не предоставлена, будут пропущены, ключи для них созданы не будут, но есть специальный флаг, который позволит сделать попытку скопировать свойство.
  6. dSalieri revised this gist Feb 25, 2023. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -19,6 +19,7 @@ function deepClone(obj, options) {

    const _null = Symbol("null");
    const map = new Map();

    return (function clone(obj, who) {
    if (!(typeof obj === "object" && obj !== null)) {
    if (who === "child" || typeof obj === "function") return obj;
    @@ -54,8 +55,8 @@ function deepClone(obj, options) {

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0) {
    if(who === "parent") return null;
    return _null;
    if(who === "parent") return null;
    return _null;
    }

    for (let key of ownKeys) {
  7. dSalieri revised this gist Feb 25, 2023. 1 changed file with 6 additions and 7 deletions.
    13 changes: 6 additions & 7 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -26,12 +26,8 @@ function deepClone(obj, options) {
    } else if (map.has(obj)) {
    return map.get(obj);
    }

    let result = who === "parent"
    ? new Object()
    : who === "child" && options.compatibleType === true
    ? new Object()
    : _null;

    let result = options.compatibleType === true ? new Object() : _null;

    switch (sortOf(obj)) {
    case "array": {
    @@ -51,7 +47,10 @@ function deepClone(obj, options) {

    map.set(obj, result);

    if (result === _null && options.compatibleType === false && who === "child") return _null;
    if (result === _null && options.compatibleType === false) {
    if (who === "parent") return null
    return _null;
    }

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0) {
  8. dSalieri revised this gist Feb 25, 2023. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions _index.md
    Original file line number Diff line number Diff line change
    @@ -68,6 +68,10 @@ let o1 = {
    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});
    /// Смотрим на результат, можно конечно вручную в консоли потрогать для убедительности
  9. dSalieri revised this gist Feb 25, 2023. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -44,7 +44,7 @@ function deepClone(obj, options) {
    }
    default: {
    if (Object.hasOwnProperty.call(options.supplementalTypes, sortOf(obj))) {
    result = options.supplementalTypes[sortOf(obj)](obj, clone);
    return options.supplementalTypes[sortOf(obj)](obj, clone);
    }
    }
    }
    @@ -54,7 +54,10 @@ function deepClone(obj, options) {
    if (result === _null && options.compatibleType === false && who === "child") return _null;

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0 && options.compatibleType === false) return _null;
    if (ownKeys.length === 0) {
    if(who === "parent") return null;
    return _null;
    }

    for (let key of ownKeys) {
    const { descriptor, type } = descriptorWithType(obj, key);
  10. dSalieri revised this gist Feb 24, 2023. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -17,6 +17,7 @@ function deepClone(obj, options) {
    },
    };

    const _null = Symbol("null");
    const map = new Map();
    return (function clone(obj, who) {
    if (!(typeof obj === "object" && obj !== null)) {
    @@ -30,7 +31,7 @@ function deepClone(obj, options) {
    ? new Object()
    : who === "child" && options.compatibleType === true
    ? new Object()
    : null;
    : _null;

    switch (sortOf(obj)) {
    case "array": {
    @@ -50,10 +51,10 @@ function deepClone(obj, options) {

    map.set(obj, result);

    if (result === null && options.compatibleType === false && who === "child") return null;
    if (result === _null && options.compatibleType === false && who === "child") return _null;

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0 && options.compatibleType === false) return null;
    if (ownKeys.length === 0 && options.compatibleType === false) return _null;

    for (let key of ownKeys) {
    const { descriptor, type } = descriptorWithType(obj, key);
    @@ -64,7 +65,7 @@ function deepClone(obj, options) {
    ) continue;

    let cloned = clone(Reflect.get(obj, key), "child");
    if (cloned !== null) {
    if (cloned !== _null) {
    if (type === "data" && ["data", "both"].some((v) => v === options.descriptorType)) {
    Reflect.defineProperty(result, key, { ...descriptor, value: cloned });
    } else if (type === "accessor" && ["accessor", "both"].some((v) => v === options.descriptorType)) {
  11. dSalieri revised this gist Feb 24, 2023. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -57,14 +57,13 @@ function deepClone(obj, options) {

    for (let key of ownKeys) {
    const { descriptor, type } = descriptorWithType(obj, key);

    if (
    (descriptor.enumerable === false && options.descriptorProps.enumerable === false) ||
    (descriptor.writable === false && options.descriptorProps.writable === false) ||
    (descriptor.configurable === false && options.descriptorProps.configurable === false)
    ) continue;

    let cloned = clone(obj[key], "child");
    let cloned = clone(Reflect.get(obj, key), "child");
    if (cloned !== null) {
    if (type === "data" && ["data", "both"].some((v) => v === options.descriptorType)) {
    Reflect.defineProperty(result, key, { ...descriptor, value: cloned });
  12. dSalieri revised this gist Feb 24, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion _index.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    **Опции:**
    ```JavaScript
    {
    prototype: true/false, /// копирует ссылку на прототип в копируемый объект
    prototype: true/false/null, /// копирует ссылку на прототип в копируемый объект, null - установка прототипа в значение null
    compatibleType: true/false, /// данная опция делает попытку скопировать объект, тип которого не поддерживается, если попытка неудачна свойство не будет создано
    descriptorType: "both"/"data"/"accessor", /// от указанного типа зависит копирование свойств конкретного типа
    descriptorProps: {
  13. dSalieri revised this gist Feb 24, 2023. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,8 @@ function deepClone(obj, options) {
    if (!(typeof obj === "object" && obj !== null)) {
    if (who === "child" || typeof obj === "function") return obj;
    else throw Error("Can't clone primitive value");
    } else if (map.has(obj)) {
    return map.get(obj);
    }

    let result = who === "parent"
  14. dSalieri revised this gist Feb 24, 2023. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions _index.md
    Original file line number Diff line number Diff line change
    @@ -19,14 +19,15 @@
    writable: true/false,
    enumerable: true/false,
    configurable: true/false,
    }
    },
    supplementalTypes: {} /// в объекте указываются специальные методы, которые реализуют специальные объекты
    }
    ```
    Советую поэкспериментировать чтобы четко понять как работают данные флаги.

    **Как добавить поддержку специального типа (на примере _`Set`_, _`Map`_ и _`Date`_):**

    Нужно передать во второй аргумент объект со свойством supplementalTypes где свойства это имена типов данных, например для типа _`Set`_, свойство `set`, для _`Map`_ свойство `map`, для _`Date`_ свойство `date` итд. Функция, которая устанавливается такому свойству может быть определена как угодно. Параметры у функции: первый это объект, который клонируется, второй это клонируемая функция из алгоритма.
    Нужно передать во второй аргумент объект со свойством `supplementalTypes` где свойства это имена типов данных, например для типа _`Set`_, свойство `set`, для _`Map`_ свойство `map`, для _`Date`_ свойство `date` итд. Функция, которая устанавливается такому свойству может быть определена как угодно. Параметры у функции: первый это объект, который клонируется, второй это клонируемая функция из алгоритма.
    ```JavaScript
    const cloneObject = {
    set: function (object, cloneF) {
  15. dSalieri created this gist Feb 24, 2023.
    74 changes: 74 additions & 0 deletions _index.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    **Цель:** Добиться копирования объекта в глубину.

    **Примечания:**
    1. Копирование работает с: _`undefined`_, _`null`_, _`number`_, _`string`_, _`boolean`_, _`array`_, _`object`_.
    2. Копирование не работает с: _`symbol`_, _`function`_. Они переносятся в клонируемый объект как есть (это из-за того как устроены внутри).
    3. Копирование поддерживает другие сложные объекты, но реализация лежит на ваших плечах (внизу есть пример как это сделать).
    4. Поддержка тех типов что не предоставлена, будут пропущены, ключи для них созданы не будут, но есть специальный флаг, который позволит сделать попытку скопировать свойство.
    5. Копирование работает со всеми типами свойств, дескриптор каждого свойства учитывается.

    **Опции:**
    ```JavaScript
    {
    prototype: true/false, /// копирует ссылку на прототип в копируемый объект
    compatibleType: true/false, /// данная опция делает попытку скопировать объект, тип которого не поддерживается, если попытка неудачна свойство не будет создано
    descriptorType: "both"/"data"/"accessor", /// от указанного типа зависит копирование свойств конкретного типа
    descriptorProps: {
    /// если указывается false то это означает что свойство с данным дескриптором и со значением false не учитывается, а значит не копируется;
    /// если указывается true, тогда не играет роли какое значение имеет свойство true или false - оно будет скопировано
    writable: true/false,
    enumerable: true/false,
    configurable: true/false,
    }
    }
    ```
    Советую поэкспериментировать чтобы четко понять как работают данные флаги.

    **Как добавить поддержку специального типа (на примере _`Set`_, _`Map`_ и _`Date`_):**

    Нужно передать во второй аргумент объект со свойством supplementalTypes где свойства это имена типов данных, например для типа _`Set`_, свойство `set`, для _`Map`_ свойство `map`, для _`Date`_ свойство `date` итд. Функция, которая устанавливается такому свойству может быть определена как угодно. Параметры у функции: первый это объект, который клонируется, второй это клонируемая функция из алгоритма.
    ```JavaScript
    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());
    },
    };
    ```

    **Тест (не забываем взять из исходника выше реализованные типы данных)**:
    ```JavaScript
    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"]])
    };
    /// Клонируем
    let cloned = deepClone(o1, {supplementalTypes: cloneObject});
    /// Смотрим на результат, можно конечно вручную в консоли потрогать для убедительности
    console.log(o1.list[5] === cloned.list[5]); /// expected: false
    ```
    95 changes: 95 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,95 @@
    function deepClone(obj, options) {
    options = {
    prototype: true,
    compatibleType: true,
    descriptorType: "both",
    ...{
    ...options,
    descriptorProps: {
    writable: true,
    enumerable: true,
    configurable: true,
    ...options?.descriptorProps,
    },
    supplementalTypes: {
    ...options?.supplementalTypes
    },
    },
    };

    const map = new Map();
    return (function clone(obj, who) {
    if (!(typeof obj === "object" && obj !== null)) {
    if (who === "child" || typeof obj === "function") return obj;
    else throw Error("Can't clone primitive value");
    }

    let result = who === "parent"
    ? new Object()
    : who === "child" && options.compatibleType === true
    ? new Object()
    : null;

    switch (sortOf(obj)) {
    case "array": {
    result = new Array();
    break;
    }
    case "object": {
    result = new Object();
    break;
    }
    default: {
    if (Object.hasOwnProperty.call(options.supplementalTypes, sortOf(obj))) {
    result = options.supplementalTypes[sortOf(obj)](obj, clone);
    }
    }
    }

    map.set(obj, result);

    if (result === null && options.compatibleType === false && who === "child") return null;

    const ownKeys = Reflect.ownKeys(obj);
    if (ownKeys.length === 0 && options.compatibleType === false) return null;

    for (let key of ownKeys) {
    const { descriptor, type } = descriptorWithType(obj, key);

    if (
    (descriptor.enumerable === false && options.descriptorProps.enumerable === false) ||
    (descriptor.writable === false && options.descriptorProps.writable === false) ||
    (descriptor.configurable === false && options.descriptorProps.configurable === false)
    ) continue;

    let cloned = clone(obj[key], "child");
    if (cloned !== null) {
    if (type === "data" && ["data", "both"].some((v) => v === options.descriptorType)) {
    Reflect.defineProperty(result, key, { ...descriptor, value: cloned });
    } else if (type === "accessor" && ["accessor", "both"].some((v) => v === options.descriptorType)) {
    Reflect.defineProperty(result, key, { ...descriptor });
    }
    }
    }

    if (options.prototype === true || options.prototype === null) {
    Reflect.setPrototypeOf(result, options.prototype === null ? null : Reflect.getPrototypeOf(obj));
    }

    return result;
    })(obj, "parent");

    function sortOf(arg) {
    return Object.prototype.toString.call(arg).slice(8, -1).toLowerCase();
    }

    function descriptorWithType(obj, prop) {
    const desc = Reflect.getOwnPropertyDescriptor(obj, prop);
    const result = { descriptor: desc, type: undefined };

    if (["writable", "value"].some((v) => Reflect.has(desc, v))) result.type = "data";
    else if (["get", "set"].some((v) => Reflect.has(desc, v))) result.type = "accessor";

    return result;
    }
    }