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

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

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

Комментарии к вопросу (23)
Решение

Для этого для любого объекта в JavaScript не будет простой и прямолинейной. Вы столкнетесь с проблемой, ошибочно подбирая атрибуты объекта's в опытном образце, который должен быть в прототип и не копируются в новый экземпляр. Если, например, вы добавляете метод клон до объекта.прототипа, а некоторые ответы изобразить, вам необходимо в явном виде отказаться от этого атрибута. Но что, если существуют другие дополнительные методы для объекта.прототип, или прочими промежуточными прототипы, что вы Дон'т знать? В этом случае, вы будете копировать атрибуты, которые вы должны'т, так что вы должны обнаружить непредвиденные, не местные атрибуты [метод hasOwnProperty`]1 метод.

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

Еще одна загвоздка в поисках элегантное решение проблемы постановки правильного прототип наследования. Если исходный объект'ы прототипа "объект", а затем просто создать новый общий объект с {} будет работать, но если источник'с прототипом является одним из потомков объект, то вы собираетесь быть пропущены дополнительных членов от того прототипа, который вы пропустили с помощью метода hasOwnPropertyфильтр, или которые были в прототипе, но там'т перечисляемую в первую очередь. Одним из решений может быть, чтобы назвать объект источник'собственность сконструктор, чтобы получить исходный объект копирования, а затем скопировать атрибуты, но тогда вы все равно не получите неперечислимый атрибутами. Например, [дата`]2 объект хранит свои данные как скрытый член:

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);

Строки даты для Д1 будет 5 секунд отстает от Д2. Как сделать одну "дата" совпадает с другим-это по телефону setTime метод, но что является специфическим для класса "дата". Я не'т думаю, что есть пуленепробиваемые общее решение этой проблемы, хотя я бы рад ошибиться!

Когда мне нужно было реализовать общие глубокого копирования я закончил ущерба, предполагая, что мне нужно будет лишь скопировать объект равнина`,время,дата,строка,число, илилогическое. Последние 3 типы являются неизменными, так что я могу выполнять мелкую копию и не волноваться о ее смене. Я сразу предположил, что какие-либо элементы, содержащиеся вобъектиливремя` также будет одним из 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, но это может быть достаточным для многих целей, как долго, как вы Дон'т предположить, что это будет работать для все, что вы бросаете на нее.

Комментарии (29)

Если вы не используете дата, функции неопределены, или бесконечность внутри объекта, очень простой лайнерв формате JSON.разобрать(в формате JSON.преобразовать в строки(объекта))`:

в

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

в

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

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

Комментарии (16)

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

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

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

Или сделать глубокая копия:

var copiedObject = jQuery.extend(true, {}, originalObject)
Комментарии (12)

В ECMAScript 6 есть объект.назначить метод, который копирует значения всех перечислимых свойств из одного объекта в другой. Например:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

Но следует помнить, что вложенные объекты будут скопированы в качестве ссылки.

Комментарии (9)

В уведомление:

  • Если вы хотите мелкую копию, использовать объект.назначить({}, а)`
  • Для на "глубокий" не копировать, не использовать формат JSON.разобрать(в формате JSON.преобразовать в строки(а))`

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

Комментарии (5)

Есть много ответов, но никто не упоминает объект.создать из ECMAScript 5, который, по общему признанию, не дает вам точную копию, но устанавливает источник в качестве прототипа нового объекта.

Таким образом, это не точный ответ на вопрос, но это решение и, таким образом, элегантно. И это работает лучше для 2 случаев:

  1. Где такое наследование полезно (да!)
  2. Где исходный объект выиграл'т быть изменен, таким образом делая связь между 2 объекты, не проблема.

Пример:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

Почему я считаю это решение выше? Это'родные с таким образом нет циклов, ни рекурсии. Однако, старые браузеры понадобится полифилл.

Комментарии (13)

Элегантный способ, чтобы клонировать объект JavaScript в одну строку кода

Для Себя Объекта.способ назначения является частью спецификации ECMAScript 2015 (ES6 в) стандартные и делает именно то, что вам нужно.

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

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

Читать more...

В полифилл для поддержки старых браузеров:

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;
    }
  });
}
Комментарии (5)

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

отношение

Я хочу глубокий-копия в JavaScript "объект" со всеми своими детьми и их детьми и так далее. Но так как я'м Не вроде нормальный застройщик, мой "объект" нормальный свойства, `кольцевые структуры "и даже" вложенных объектов.

Так давайте'ы создание кольцевой структуры " и " вложенный объект первым.

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

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

Позвольте's и принести все вместе в "объект" по имени "а".

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('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';

Дон'т тратить слишком много времени на это, вы'll получить ошибку TypeError: преобразование круговой структуры в 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.");
}

Выглядит хорошо, хех? Это'ы рекурсивную копию объекта и ручки, а также другие виды, такие как дата, но это было'т требование.

var b = cloneSO(a);

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

Рекурсия и круглых колодцев не'т хорошо работать вместе... RangeError: максимальный размер стека вызовов превысило

решения #

После ссоры с коллегой, мой босс спросил нас, что случилось, и он нашел простое решение после некоторых погуглить. Это'ы называемого Объекта.создать`.

var b = Object.create(a);

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

Этот раствор добавляли некоторое время назад в JavaScript и даже ручки круглое.

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"
    }
}

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

полифилл для собственного решения

Там'ы полифилла для объекта`.создать в старых браузера, как и в IE 8. Это's что-то вроде рекомендованной Mozilla, и конечно, это's не совершенный и результаты в те же проблемы, как решения.

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

var b = clonePF(a);

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

Я'ве положить Ф вне рамок, так что мы можем взглянуть на то, что экземпляром говорит нам.

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

Такая же проблема, как родной раствора, но немного хуже выход.

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

При рытье вокруг, я нашел похожий вопрос (https://stackoverflow.com/questions/10728412/in-javascript-when-performing-a-deep-copy-how-do-i-avoid-a-cycle-due-to-a-pro) в этом одним, но с лучшим решением.


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
Комментарии (7)

Если вы'ре хорошо с мелкой копией, библиотекой Underscore.js имеет клон метод.

y = _.clone(x);

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

copiedObject = _.extend({},originalObject);
Комментарии (3)

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

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

или

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

ответ в основном depeneds, на котором функции вы, используя, в ЕС6+, вы можете просто использовать объект.назначить на клона:

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

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

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

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

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
Комментарии (5)

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

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

Эта методика идеально подходит для моего использования, так как я'м хранить в JSON BLOB-объектов в хранилище ключ-значение, и, когда они представляются как объекты в 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
Комментарии (4)

Вы можете просто использовать спред собственность, чтобы скопировать объект без ссылок. Но будьте внимательны (см. Комментарии), 'скопировать' это просто на самом низком объекта/массива на уровне. Вложенные свойства-это все ссылки!


Полный клон:

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:

Комментарии (3)

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

var destination = angular.copy(source);

или

angular.copy(source, destination);

Больше в угловой.копия документации...

Комментарии (1)

А. Леви'ы ответ почти полный, вот мой маленький вклад: есть способ, как справиться с рекурсивными ссылками см. Эта линия

если(это[м]==это) копировать[м] = копия;

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

если(это.помощью clonenode) вернуть это.помощью clonenode(true); в`

Вдохновленный А. Леви'исчерпывающего исследования с и Calvin'ы подход прототипирование, я предлагаю такое решение:

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;
}

См. также Энди Берк's примечание в ответах.

Комментарии (2)

Из этой статьи: Как копировать массивы и объекты в Javascript by Brian Huisman:

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;
};
Комментарии (6)

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

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;
}
Комментарии (4)

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

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

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

Комментарии (6)

В ECMAScript 2018

let objClone = { ...obj };

Помните, что вложенные объекты еще копировать в качестве эталона.

Комментарии (4)

Новый ответ на старый вопрос! Если у вас есть удовольствие с помощью ECMAScript 2016 (ЕС6) с распространение синтаксис, Это's легко.

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

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

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

Комментарии (4)

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

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();
    };
}
Комментарии (7)