У меня есть объект x
. Я хотел бы скопировать его как объект y
, чтобы изменения y
не изменялись x
. Я понял, что копирование объектов, полученных из встроенных объектов JavaScript, приведет к появлению дополнительных нежелательных свойств. Это не проблема, так как я копирую один из своих собственных объектов, созданных в буквальном смысле.
Как правильно клонировать объект JavaScript?
javascript
clone
javascript-objects
soundly_typed
источник
источник
mObj=JSON.parse(JSON.stringify(jsonObject));
Object.create(o)
, он делает все, что просит автор?var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;
После этогоy.deep.key
также будет 2, следовательно, Object.create НЕ МОЖЕТ ИСПОЛЬЗОВАТЬСЯ для клонирования ...Ответы:
Сделать это для любого объекта в JavaScript не будет просто или просто. Вы столкнетесь с проблемой ошибочного выбора атрибутов из прототипа объекта, которые следует оставить в прототипе и не копировать в новый экземпляр. Например, если вы добавляете
clone
методObject.prototype
, как показывают некоторые ответы, вам нужно явно пропустить этот атрибут. Но что, если есть другие дополнительные методыObject.prototype
или промежуточные прототипы, о которых вы не знаете? В этом случае вы скопируете атрибуты, которые не следует делать, поэтому вам необходимо обнаружить непредвиденные нелокальные атрибуты с помощьюhasOwnProperty
метода.Помимо неперечислимых атрибутов, вы столкнетесь с более сложной проблемой, когда попытаетесь скопировать объекты со скрытыми свойствами. Например,
prototype
это скрытое свойство функции. Кроме того, на прототип объекта ссылается атрибут__proto__
, который также скрыт и не будет скопирован циклом for / in, повторяющимся по атрибутам исходного объекта. Я думаю, что это__proto__
может быть специфично для интерпретатора JavaScript Firefox, и это может быть что-то другое в других браузерах, но вы получите картину. Не все перечисляемо. Вы можете скопировать скрытый атрибут, если знаете его имя, но я не знаю, как его обнаружить автоматически.Еще одним препятствием в поиске элегантного решения является проблема правильной настройки наследования прототипа. Если у прототипа вашего исходного объекта есть
Object
, то просто создастся новый общий объект с{}
, но если у прототипа источника есть какой-то потомокObject
, то вы пропустите дополнительные члены из этого прототипа, который вы пропустили с помощьюhasOwnProperty
фильтра, или которые были в прототипе, но не были перечисляемыми в первую очередь. Одним из решений может быть вызовconstructor
свойства исходного объекта, чтобы получить начальный объект копирования, а затем скопировать атрибуты, но тогда вы все равно не получите неперечислимые атрибуты. Например,Date
объект хранит свои данные как скрытый член:Строка даты для
d1
будет на 5 секунд позжеd2
. Один из способов сделать одно иDate
то же - это вызватьsetTime
метод, но это зависит отDate
класса. Я не думаю, что есть пуленепробиваемое общее решение этой проблемы, хотя я был бы рад ошибаться!Когда я должен был осуществлять общее глубокое копирование я в конечном итоге под угрозу, если предположить , что я только нужно скопировать простой
Object
,Array
,Date
,String
,Number
, илиBoolean
. Последние 3 типа являются неизменяемыми, поэтому я мог выполнить поверхностное копирование и не беспокоиться о его изменении. Кроме того, я предположил, что любые элементы, содержащиеся вObject
илиArray
будут одним из 6 простых типов в этом списке. Это может быть выполнено с помощью кода, подобного следующему:Вышеприведенная функция будет работать адекватно для 6 упомянутых мною простых типов, если данные в объектах и массивах образуют древовидную структуру. То есть в объекте не более одной ссылки на одни и те же данные. Например:
Он не сможет обработать какой-либо объект JavaScript, но этого может быть достаточно для многих целей, если вы не предполагаете, что он будет работать только для всего, что вы ему добавите.
источник
JSON.parse(JSON.stringify([some object]),[some revirer function])
время решением проблемы?var cpy = new obj.constructor()
?Если вы не используете
Date
s, functions, undefined, regExp или Infinity в вашем объекте, очень простой вкладышJSON.parse(JSON.stringify(object))
:Это работает для всех видов объектов, содержащих объекты, массивы, строки, логические значения и числа.
См. Также эту статью о алгоритме структурированного клонирования браузеров, который используется при отправке сообщений сотруднику и от него. Он также содержит функцию для глубокого клонирования.
источник
С jQuery вы можете копировать с расширением :
последующие изменения
copiedObject
не повлияют наoriginalObject
, и наоборот.Или сделать глубокую копию :
источник
var copiedObject = jQuery.extend({},originalObject);
jQuery.extend(true, {}, originalObject);
...subsequent changes to the copiedObject will not affect the originalObject, and vice versa...
. Извините, что я был действительно смущен.В ECMAScript 6 есть метод Object.assign , который копирует значения всех перечисляемых собственных свойств из одного объекта в другой. Например:
Но имейте в виду, что вложенные объекты все еще копируются как ссылки.
источник
Object.assign
это путь. Это также легко заполнить: gist.github.com/rafaelrinaldi/43813e707970bd2d77faв MDN :
Object.assign({}, a)
JSON.parse(JSON.stringify(a))
Нет необходимости во внешних библиотеках, но сначала нужно проверить совместимость браузера .
источник
clone
функциональность. узнать больше здесь momentjs.com/docs/#/parsing/moment-cloneЕсть много ответов, но ни один из них не упоминает Object.create из ECMAScript 5, который, по общему признанию, не дает точной копии, но устанавливает источник в качестве прототипа нового объекта.
Таким образом, это не точный ответ на вопрос, но это однострочное решение и, следовательно, элегантный. И это лучше всего работает в 2 случаях:
Пример:
Почему я считаю это решение лучшим? Он родной, поэтому нет циклов, нет рекурсии. Тем не менее, старые браузеры будут нуждаться в polyfill.
источник
var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
Object.create
указывает на свойства родителя через ссылки. Это означает, что если значения свойств родителя изменятся, то и потомок также изменится. Это имеет некоторые неожиданные побочные эффекты с вложенными массивами и объектами, которые могут привести к трудным для обнаружения ошибкам в вашем коде, если вы о них не знаете: jsbin.com/EKivInO/2 . Клонированный объект - это совершенно новый, независимый объект, который имеет те же свойства и значения, что и родительский объект, но не связан с родительским объектом.Элегантный способ клонировать объект Javascript в одну строку кода
Object.assign
Метод является частью 2015 (ES6) стандарта ECMAScript и делает именно то , что вам нужно.Читать далее...
Polyfill для поддержки старых браузеров:
источник
Object.assign
используются два параметра, когдаvalue
функция в полифилле принимает только один параметр?arguments
объектом раньше.) У меня проблемы с поискомObject()
через Google ... это типизированная передача, не так ли?Есть несколько проблем с большинством решений в Интернете. Поэтому я решил сделать продолжение, которое включает, почему принятый ответ не должен быть принят.
стартовая ситуация
я бы хотел глубоко скопировать Javascript
Object
со всеми его детьми и их детьми и так далее. Но так как я не нормальный разработчик, у меняObject
есть нормальныйproperties
,circular structures
и дажеnested objects
.Итак, давайте создадим
circular structure
иnested object
первый.Давайте соберем все вместе по
Object
имениa
.Далее мы хотим скопировать
a
в переменную с именемb
и изменить ее.Вы знаете, что здесь произошло, потому что если бы не вы, вы бы даже не попали на этот великий вопрос.
Теперь давайте найдем решение.
JSON
Первая попытка, которую я попробовал, использовала
JSON
.Не тратьте слишком много времени на это, вы получите
TypeError: Converting circular structure to JSON
.Рекурсивная копия (принятый «ответ»)
Давайте посмотрим на принятый ответ.
Выглядит хорошо, а? Это рекурсивная копия объекта, которая также обрабатывает другие типы
Date
, но это не было обязательным требованием.Рекурсия и
circular structures
не очень хорошо работают вместе ...RangeError: Maximum call stack size exceeded
нативное решение
После спора с моим коллегой, мой начальник спросил нас, что случилось, и он нашел простое решение после некоторого поиска в Google. Это называется
Object.create
.Это решение было добавлено в Javascript некоторое время назад и даже обрабатывает
circular structure
.... и вы видите, это не сработало с вложенной структурой внутри.
полифилл для нативного раствора
В
Object.create
старом браузере, как и в IE 8 , есть полифилл. Это что-то вроде рекомендованного Mozilla, и, конечно, оно не идеально и приводит к той же проблеме, что и нативное решение .Я
F
вышел за рамки, чтобы мы могли взглянуть на то, чтоinstanceof
говорит нам.Та же проблема, что и у нативного решения , но немного худший результат.
лучшее (но не идеальное) решение
Копаясь, я нашел похожий вопрос ( в Javascript, когда я выполняю глубокое копирование, как мне избежать цикла из-за свойства «this»? ) На этот, но с более лучшим решением.
И давайте посмотрим на вывод ...
Требования соответствуют, но все еще есть некоторые меньшие проблемы, включая изменение
instance
ofnested
иcirc
toObject
.вывод
Последнее решение, использующее рекурсию и кеш, может быть не лучшим, но это настоящая глубокая копия объекта. Он обрабатывает простой
properties
,circular structures
иnested object
, но это испортит экземпляр из них при клонировании.jsfiddle
источник
Если у вас все в порядке с мелкой копией, в библиотеке underscore.js есть метод clone .
или вы можете расширить его как
источник
Хорошо, представьте, что у вас есть этот объект ниже, и вы хотите его клонировать:
или
ответ в основном depeneds , на котором ECMAScript вы используете, в
ES6+
, вы можете просто использовать ,Object.assign
чтобы сделать клон:или используя оператор распространения, как это:
Но если вы используете
ES5
, вы можете использовать несколько методов, ноJSON.stringify
, просто убедитесь, что вы не используете для копирования большой кусок данных, но во многих случаях это может быть удобной однострочной строкой, что-то вроде этого:источник
big chunk of data
бы приравнять? 100kb? 100MB? Спасибо!Object.assign
делает мелкую копию (так же, как распространение, @Alizera)Одним из особенно не элегантных решений является использование кодировки JSON для создания глубоких копий объектов, которые не имеют методов-членов. Методология состоит в том, чтобы JSON кодировать ваш целевой объект, а затем, расшифровав, вы получите искомую копию. Вы можете декодировать столько раз, сколько хотите, чтобы сделать столько копий, сколько вам нужно.
Конечно, функции не принадлежат JSON, поэтому это работает только для объектов без методов-членов.
Эта методология идеально подошла для моего случая использования, поскольку я храню большие двоичные объекты JSON в хранилище значений ключей, и когда они представляются как объекты в API JavaScript, каждый объект фактически содержит копию исходного состояния объекта, поэтому мы может вычислить дельту после того, как вызывающая сторона видоизменила экспонированный объект.
источник
{ 'foo': function() { return 1; } }
объект, созданный в буквальном смысле .Вы можете просто использовать свойство распространения, чтобы скопировать объект без ссылок. Но будьте осторожны (см. Комментарии), «копия» находится на самом низком уровне объекта / массива. Вложенные свойства все еще являются ссылками!
Полный клон:
Клон со ссылками на второй уровень:
JavaScript на самом деле не поддерживает глубокие клоны изначально. Используйте служебную функцию. Например Рамда:
источник
const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Для тех, кто использует AngularJS, существует также прямой метод клонирования или расширения объектов в этой библиотеке.
или
Больше в документации angular.copy ...
источник
Вот функция, которую вы можете использовать.
источник
new
. Принятый ответ не дает.Ответ А. Леви почти полный, вот мой маленький вклад: есть способ, как обрабатывать рекурсивные ссылки , см. Эту строку
if(this[attr]==this) copy[attr] = copy;
Если объект является XML-элементом DOM, мы должны использовать вместо него cloneNode
if(this.cloneNode) return this.cloneNode(true);
Вдохновленный исчерпывающим исследованием А. Леви и подходом Келвина к созданию прототипов, я предлагаю следующее решение:
Смотрите также заметку Энди Бёрка в ответах.
источник
Date.prototype.clone = function() {return new Date(+this)};
Из этой статьи: Как скопировать массивы и объекты в Javascript Брайана Хуисмана:
источник
var copiedObj = Object.create(obj);
отличный способ, а?В ES-6 вы можете просто использовать Object.assign (...). Пример:
Хорошая ссылка здесь: https://googlechrome.github.io/samples/object-assign-es6/
источник
let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";
)obj
свойств действительно клонируются. Значения свойств, которые сами являются объектами, не будут клонированы.В ECMAScript 2018
Помните, что вложенные объекты по-прежнему копируются в качестве ссылки.
источник
const objDeepClone = JSON.parse(JSON.stringify(obj));
Используя Lodash:
источник
_.cloneDeep(x)
поскольку это по сути то же самое, что и выше, но читается лучше.Интересует клонирование простых объектов:
JSON.parse(JSON.stringify(json_original));
Источник: Как скопировать объект JavaScript в новую переменную НЕ по ссылке?
источник
Вы можете клонировать объект и удалить любую ссылку из предыдущего, используя одну строку кода. Просто сделайте:
Для браузеров / движков, которые в настоящее время не поддерживают Object.create, вы можете использовать этот polyfill:
источник
Object.create(...)
кажется определенно путь.Object.hasOwnProperty
? Таким образом, люди знают, как предотвратить поиск ссылки на прототип.text
участником в obj2. Вы не делаете копию, просто откладываете цепочку прототипов, когда член не найден в obj2.Новый ответ на старый вопрос! Если вы имеете удовольствие от использования ECMAScript 2016 (ES6) с синтаксисом распространения , это легко.
Это обеспечивает чистый метод для мелкой копии объекта. Создание глубокой копии, означающей создание новой копии каждого значения в каждом рекурсивно-вложенном объекте, требует более тяжелых решений, указанных выше.
JavaScript продолжает развиваться.
источник
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
Решение ES6, если вы хотите (поверхностно) клонировать экземпляр класса, а не просто объект свойства.
источник
let cloned = Object.assign({}, obj)
?Я думаю, что есть простой и рабочий ответ. При глубоком копировании есть две проблемы:
Поэтому я думаю, что одним из простых решений будет сначала сериализовать и десериализовать, а затем назначить для него функции копирования.
Хотя на этот вопрос есть много ответов, я надеюсь, что этот тоже поможет.
источник
cloneDeep
.Для глубокого копирования и клонирования JSON.stringify затем JSON.parse объект:
источник
Это адаптация кода А. Леви для обработки клонирования функций и множественных / циклических ссылок - это означает, что если два свойства в клонированном дереве являются ссылками на один и тот же объект, то клонированное дерево объектов будет иметь эти свойства указывают на один и тот же клон ссылочного объекта. Это также решает случай циклических зависимостей, которые, если оставить их необработанными, приводят к бесконечному циклу. Сложность алгоритма составляет O (n)
Несколько быстрых тестов
источник
Я просто хотел добавить ко всем
Object.create
решениям в этом посте, что это не работает желаемым образом с nodejs.В Firefox результат
является
{test:"test"}
,В nodejs это
источник
Object.hasOwnProperty
чтобы проверить, владеете ли вы массивом или нет. Да, это добавляет дополнительную сложность, чтобы иметь дело с прототипным наследованием.источник
if(!src && typeof src != "object"){
, Я думаю, что это||
не должно быть&&
.Поскольку mindeavor заявил, что клонируемый объект является «буквально сконструированным» объектом, решение может состоять в том, чтобы просто сгенерировать объект несколько раз, а не клонировать экземпляр объекта:
источник
Я написал свою собственную реализацию. Не уверен, что это считается лучшим решением:
Ниже приводится реализация:
источник