Как правильно клонировать объект JavaScript? - PullRequest
2787 голосов
/ 08 апреля 2009

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

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

Ответы [ 63 ]

38 голосов
/ 29 октября 2009

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

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

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

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

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

object2.key = "a change";
console.log(object1);// returns value
32 голосов
/ 13 апреля 2016

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


Полный клон:

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 на самом деле не поддерживает глубокие клоны. Используйте служебную функцию. Например Рамда:

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

24 голосов
/ 03 сентября 2014

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

var destination = angular.copy(source);

или

angular.copy(source, destination);

Больше в angular.copy документации ...

22 голосов
/ 26 января 2012

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

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

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

19 голосов
/ 08 апреля 2009

Из этой статьи: Как скопировать массивы и объекты в Javascript . Автор 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;
};
19 голосов
/ 08 апреля 2009

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

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;
}
18 голосов
/ 13 апреля 2017

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

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

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

16 голосов
/ 03 августа 2018

В ECMAScript 2018

let objClone = { ...obj };

Помните, что вложенные объекты по-прежнему копируются в качестве ссылки.

13 голосов
/ 26 мая 2012

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

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

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

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}
13 голосов
/ 16 декабря 2016

Новый ответ на старый вопрос! Если вы имеете удовольствие от использования ECMAScript 2016 (ES6) с Spread Syntax , это просто.

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

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

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

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