Как правильно клонировать объект JavaScript?

у меня есть объект, x. Я хотел бы скопировать его как object y, такое, что изменяется на y Не изменяйте x. Я понял, что копирование объектов, полученных из встроенных объектов JavaScript, приведет к дополнительным нежелательным свойствам. Это не проблема, так как я копирую один из моих собственных, буквально построенных объектов.

Как правильно клонировать объект JavaScript?

30 ответов


сделать это для любого объекта в JavaScript будет не просто и не просто. Вы столкнетесь с проблемой ошибочного выбора атрибутов из прототипа объекта, которые должны быть оставлены в прототипе и не скопированы в новый экземпляр. Если, например, вы добавляете clone метод Object.prototype, как показывают некоторые ответы, вам нужно будет явно пропустить этот атрибут. Но что, если есть другие дополнительные методы, добавленные в Object.prototype, или другие промежуточные прототипы, о котором ты не знаешь? В этом случае вы скопируете атрибуты, которые не должны, поэтому вам нужно обнаружить непредвиденные, нелокальные атрибуты с помощью hasOwnProperty метод.

в дополнение к не перечисляемым атрибутам, вы столкнетесь с более жесткой проблемой при попытке скопировать объекты, которые имеют скрытые свойства. Например, prototype является скрытым свойством функции. Кроме того, прототип объекта ссылается на атрибут __proto__, который также скрыт, и не будет копируется циклом for/in, повторяющим атрибуты исходного объекта. Я думаю __proto__ может быть специфичным для интерпретатора JavaScript Firefox, и это может быть что-то другое в других браузерах, но вы получаете картину. Не все можно перечислить. Вы можете скопировать скрытый атрибут, если знаете его имя, но я не знаю никакого способа обнаружить его автоматически.

еще одна загвоздка в поисках элегантного решения-проблема настройки наследования прототипа правильно. Если прототип исходного объекта Object, затем просто создать новый общий объект с {} будет работать, но если прототип источника является одним из потомков Object, тогда вам будет не хватать дополнительных членов из этого прототипа, который вы пропустили с помощью hasOwnProperty filter, или которые были в прототипе, но не были перечислимы в первую очередь. Одним из решений может быть вызов исходного объекта constructor свойство для получения объекта начальной копии, а затем скопируйте атрибуты, но тогда вы все равно не получите не перечисляемые атрибуты. Например,Date объект хранит свои данные как скрытый элемент:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

дата строка d1 будет на 5 секунд позже, чем d2. Способ сделать один Date то же самое, что и другой, вызывая setTime метод, но это специфично для Date класса. Я не думаю, что есть пуленепробиваемые общее решение этой проблемы, хотя я был бы счастлив ошибаться!

когда мне пришлось реализовать общее глубокое копирование, я в конечном итоге пошел на компромисс, предположив, что мне нужно будет только скопировать простой Object, Array, Date, String, Number или Boolean. Последние 3 типа неизменяемы, поэтому я мог бы выполнить мелкую копию и не беспокоиться об ее изменении. Далее я предположил, что любые элементы, содержащиеся в Object или Array также будет одним из 6 простых типов в этом списке. Это может быть выполнено с помощью кода, такого как следующий:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

вышеуказанная функция будет адекватно работать для 6 простых типов, о которых я упомянул, пока данные в объектах и массивах образуют древовидную структуру. То есть в объекте существует не более одной ссылки на одни и те же данные. Например:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

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


С jQuery, вы можете мелкая копия С расширения:

var copiedObject = jQuery.extend({}, originalObject)

последующие изменения copiedObject не повлияют на originalObject и наоборот.

или глубокая копия:

var copiedObject = jQuery.extend(true, {}, originalObject)

Если вы не используете функции в своем объекте, очень простой лайнер может быть следующим:

var cloneOfA = JSON.parse(JSON.stringify(a));

это работает для всех видов объектов, содержащих объекты, массивы, строки, логические значения и числа.

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


в ECMAScript 6 есть


Per MDN:

  • если вы хотите мелкую копию, используйте Object.assign({}, a)
  • для" глубокой " копии используйте JSON.parse(JSON.stringify(a))

нет необходимости во внешних библиотеках, но вам нужно проверить браузер совместимости.


есть много ответов, но ни один из них не упоминает


элегантный способ клонирования объекта Javascript в одной строке кода

An Object.assign метод является частью стандарта ECMAScript 2015 (ES6) и делает именно то, что вам нужно.

var clone = Object.assign({}, obj);

Объект.методом assign() используется для копирования значений всех собственных перечислимых свойств из одного или нескольких исходных объектов в целевой объект.

подробнее...

на polyfill для поддержки старых браузеры:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

Если вы в порядке с мелкой копией, подчеркивание.библиотека js имеет клон метод.

y = _.clone(x);

или вы можете продлить его как

copiedObject = _.extend({},originalObject);

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

исходное положение

я хочу глубокий-копия Javascript Object со всеми своими детьми и их детьми и так далее. Но поскольку я не являюсь нормальным разработчиком, мой Object и нормальный properties, circular structures и даже nested objects.

так давайте создадим circular structure и nested object первый.

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

давайте соберем все вместе вObject имени a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

Далее, мы хотим скопировать a в переменной с именем b и мутировать его.

var b = a;

b.x = 'b';
b.nested.y = 'b';

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

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

теперь давайте найдем решение.

JSON

первая попытка я пробовал был используя JSON.

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

не тратьте слишком много времени на это, вы получите TypeError: Converting circular structure to JSON.

рекурсивное копирование (принятый "ответ")

давайте посмотрим на принятый ответ.

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

хорошо выглядит, а? Это рекурсивная копия объекта и обрабатывает другие типы, такие как Date, но это не было требованием.

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

рекурсия и circular structures не работает хорошо вместе... RangeError: Maximum call stack size exceeded

собственное решение

после ссоры с коллегой, мой босс спросил нас, что случилось, и он нашел простой решение после некоторых погуглить. Это называется Object.create.

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

это решение было добавлено в Javascript некоторое время назад и даже обрабатывает circular structure.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

... и вы видите, что это не сработало с вложенной структурой внутри.

polyfill для родного решение

есть полифилл для Object.create в старом браузере, как и IE 8. Это что-то вроде рекомендованного Mozilla, и, конечно, это не идеально и приводит к той же проблеме, что и собственное решение.

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

я поставил F вне области, так что мы можем посмотреть на что instanceof говорит нам.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

та же проблема, что и собственное решение, но немного хуже выход.

лучшее (но не идеальное) решение

копаясь вокруг, я нашел аналогичный вопрос (в Javascript при выполнении глубокой копии как избежать цикла из-за свойства "это"?) к этому, но с лучшим решением.

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

и давайте посмотрим на выходе...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

требования соответствуют, но все еще некоторые более небольшие вопросы, включая изменение instance of nested и circ to Object.

структура деревьев, которые разделяют лист, не будет скопирована, они станут двумя независимыми листьями:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

вывод

последнее решение, использующее рекурсию и кэш, может быть не лучшим, но это реальные deep-копия объекта. Он обрабатывает простой properties, circular structures и nested object, но это испортит экземпляр их в то время как клонирование.

http://jsfiddle.net/einfallstoll/N4mr2/


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

конечно, функции не принадлежат JSON, поэтому это работает только для объектов без методов-членов.

эта методология идеально подходит для моего использования дело в том, что я храню JSON blobs в хранилище ключей, и когда они отображаются как объекты в JavaScript API, каждый объект фактически содержит копию исходного состояния объекта, поэтому мы можем вычислить дельту после того, как вызывающий объект мутировал открытый объект.

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

Вы можете просто использовать распространение собственность копировать объект без ссылок. Но будьте осторожны (см. комментарии), "копия" находится только на самом низком уровне объекта/массива. Вложенные свойства по-прежнему являются ссылками!


полный клон:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

клон со ссылками на втором уровне:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript фактически не поддерживает глубокие клоны изначально. Используйте служебную функцию. Например Ramda:

http://ramdajs.com/docs/#clone


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

var destination = angular.copy(source);

или

angular.copy(source, destination);

больше угловых.копия документация...


ОК представьте, что у вас есть этот объект, и вы хотите клонировать его:

let obj = {a:1, b:2, c:3}; //ES6

или

var obj = {a:1, b:2, c:3}; //ES5

ответ в основном depeneds, на котором ECMAscript вы используете, в ES6+, вы можете просто использовать Object.assign сделать клон:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

или с помощью операторов такой:

let cloned = {...obj}; //new {a:1, b:2, c:3};

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

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

ответ А. Леви почти завершен, вот мой маленький вклад:существует способ обработки рекурсивных ссылок см. В этой строке

if(this[attr]==this) copy[attr] = copy;

Если объект является элементом XML DOM, мы должны использовать помощью clonenode

if(this.cloneNode) return this.cloneNode(true);

вдохновленный исчерпывающим исследованием А. Леви и подходом прототипирования Кальвина, я предлагаю следующее решение:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

см. Также Примечание Энди Берка в ответы.


из этой статьи: Как копировать массивы и объекты в JavaScript Брайан Хусман:

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (var i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
};

в ES-6 Вы можете просто использовать Object.назначать.(..). Ex:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

хорошая ссылка здесь: https://googlechrome.github.io/samples/object-assign-es6/


вот функция, которую вы можете использовать.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

вы можете клонировать объект и удалить любую ссылку из предыдущей, используя одну строку кода. Попросту:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

для браузеров / движков, которые в настоящее время не поддерживают объект.создать Вы можете использовать этот полифилл:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

новый ответ на старый вопрос! Если у вас есть удовольствие от использования ECMAScript 2016 (ES6) с Распространение Синтаксис, это легко.

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

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

код JavaScript продолжает развиваться.


Использование Lodash:

var y = _.clone(x, true);

let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

решение ES6, если вы хотите (мелкий) клонировать a экземпляр класса и не просто объект недвижимости.


интересует клонирование простых объектов :

в формате JSON.разобрать(в формате JSON.stringify (json_original));

источник : как скопировать объект JavaScript в новую переменную не по ссылке?


ответ Яна Туроня выше очень близок и может быть лучшим для использования в браузере из-за проблем совместимости, но это потенциально вызовет некоторые странные проблемы перечисления. Например, выполнение:

for ( var i in someArray ) { ... }

назначит метод clone () i после итерации по элементам массива. Вот адаптация, которая позволяет избежать перечисления и работает с node.js:

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

это позволяет избежать перечисления метода clone (), поскольку defineProperty() по умолчанию используется значение false.


это адаптация кода А. Леви также для обработки клонирования функций и множественных/циклических ссылок - это означает, что если два свойства в дереве, которое клонируется, являются ссылками одного и того же объекта, клонированное дерево объектов будет иметь эти свойства, указывающие на один и тот же клон ссылочного объекта. Это также решает случай циклических зависимостей, которые, если их не обрабатывать, приводят к бесконечному циклу. Сложность алгоритма O (n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

некоторые быстрые тесты

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));

Я написал свою реализацию. Не уверен, что это считается лучшим решением:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

следующая реализация:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

Я просто хотел добавить ко всем Object.create решения в этом посте, что это не работает желаемым образом с nodejs.

в Firefox результат

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

is

{test:"test"}.

в nodejs это

{}

function clone(src, deep) {

    var toString = Object.prototype.toString;
    if(!src && typeof src != "object"){
        //any non-object ( Boolean, String, Number ), null, undefined, NaN
        return src;
    }

    //Honor native/custom clone methods
    if(src.clone && toString.call(src.clone) == "[object Function]"){
        return src.clone(deep);
    }

    //DOM Elements
    if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
        return src.cloneNode(deep);
    }

    //Date
    if(toString.call(src) == "[object Date]"){
        return new Date(src.getTime());
    }

    //RegExp
    if(toString.call(src) == "[object RegExp]"){
        return new RegExp(src);
    }

    //Function
    if(toString.call(src) == "[object Function]"){
        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });

    }

    var ret, index;
    //Array
    if(toString.call(src) == "[object Array]"){
        //[].slice(0) would soft clone
        ret = src.slice();
        if(deep){
            index = ret.length;
            while(index--){
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }

    return ret;
};

С mindeavor заявил, что объект для клонирования является "литерал-построенным" объектом, решение может быть просто создать объект несколько раз, а не клонирование экземпляра объекта:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();

проконсультироваться http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data для алгоритма "безопасной передачи структурированных данных" W3C, предназначенного для реализации браузерами для передачи данных, например, веб-работникам. Однако у него есть некоторые ограничения, поскольку он не обрабатывает функции. См.https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm для получения дополнительной информации, включая альтернативный алгоритм в JS что отчасти и помогает тебе туда добраться.


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

https://github.com/radsimu/UaicNlpToolkit/blob/master/Modules/GGS/GGSEngine/src/main/resources/ro/uaic/info/nlptools/ggs/engine/core/jsInitCode.js#L17