Как сделать глубокий клон в JavaScript - PullRequest
87 голосов
/ 16 декабря 2010

Как вы глубоко клонируете объект Javascript?

Я знаю, что существуют различные функции, основанные на таких фреймворках, как JSON.parse(JSON.stringify(o)) и $.extend(true, {}, o), но я не хочу использовать такие фреймворки.

Какой самый элегантный или эффективный способ создания глубокого клона.

Мы заботимся о крайних случаях, таких как клонирование массивов. Не ломать цепочки прототипов, заниматься самообращением.

Мы не заботимся о поддержке копирования объектов DOM и т. Д., Поскольку по этой причине существует .cloneNode.

Поскольку я в основном хочу использовать глубокие клоны в node.js, использование функций ES5 движка V8 приемлемо.

[Изменить]

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

[Дальнейшее редактирование]

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

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

Есть ли способ написать функцию-клон, которая клонирует объект, имеет такое же состояние во время клонирования, но не может изменить состояние o без написания парсера JS в JS.

В такой функции больше не должно быть необходимости в реальном мире. Это просто академический интерес.

Ответы [ 14 ]

0 голосов
/ 21 декабря 2018

мое дополнение ко всем ответам

deepCopy = arr => {
  if (typeof arr !== 'object') return arr
  if(arr.pop) return [...arr].map(deepCopy)
  const copy = {}
  for (let prop in arr)
    copy[prop] = deepCopy(arr[prop])
  return copy
}
0 голосов
/ 22 апреля 2018

В такой функции больше не должно быть необходимости в реальном мире. Это просто академический интерес.

В качестве чисто упражнения это более функциональный способ выполнения. Это расширение ответа @ tfmontague как Я бы предложил добавить туда защитный блок. Но, учитывая, что я чувствую себя обязанным к ES6 и функционализирую все, вот моя версия с сутенерством. Это усложняет логику, так как вам приходится отображать массив и уменьшать объект, но избегает любых мутаций.

function cloner(x) {
    const recurseObj = x => typeof x === 'object' ? cloner(x) : x
    const cloneObj = (y, k) => {
        y[k] = recurseObj(x[k])
        return y
    }
    // Guard blocks
    // Add extra for Date / RegExp if you want
    if (!x) {
        return x
    }
    if (Array.isArray(x)) {
        return x.map(recurseObj)
    }
    return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
    null,
    [],
    {},
    [1,2,3],
    [1,2,3, null],
    [1,2,3, null, {}],
    [new Date('2001-01-01')], // FAIL doesn't work with Date
    {x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
    {
        obj : new function() {
            this.name = "Object test";
        }
    } // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))
0 голосов
/ 10 марта 2018

Мы можем использовать рекурсию для создания deepCopy.Может создавать копию массива, объекта, массива объекта, объекта с функцией.если вы хотите, вы можете добавить функцию для другого типа структуры данных, например карты и т. д.

function deepClone(obj) {
         var retObj;
        _assignProps = function(obj, keyIndex, retObj) {
               var subType = Object.prototype.toString.call(obj[keyIndex]);
               if(subType === "[object Object]" || subType === "[object Array]") {
                    retObj[keyIndex] = deepClone(obj[keyIndex]);
               }
               else {
                     retObj[keyIndex] = obj[keyIndex];
               }
        };

        if(Object.prototype.toString.call(obj) === "[object Object]") {
           retObj = {};
           for(key in obj) {
               this._assignProps(obj, key, retObj);
           }
        }
        else if(Object.prototype.toString.call(obj) == "[object Array]") {
           retObj = [];
           for(var i = 0; i< obj.length; i++) {
              this._assignProps(obj, i, retObj);
            }
        };

        return retObj;
    };
0 голосов
/ 20 декабря 2017

Это работает для массивов, объектов и примитивов. Вдвойне рекурсивный алгоритм, который переключается между двумя методами обхода:

const deepClone = (objOrArray) => {

  const copyArray = (arr) => {
    let arrayResult = [];
    arr.forEach(el => {
        arrayResult.push(cloneObjOrArray(el));
    });
    return arrayResult;
  }

  const copyObj = (obj) => {
    let objResult = {};
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        objResult[key] = cloneObjOrArray(obj[key]);
      }
    }
    return objResult;
  }

  const cloneObjOrArray = (el) => {
    if (Array.isArray(el)) {
      return copyArray(el);
    } else if (typeof el === 'object') {
      return copyObj(el);
    } else {
      return el;
    }
  }

  return cloneObjOrArray(objOrArray);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...