Каков наиболее эффективный способ клонирования объекта JavaScript? Я видел, obj = eval(uneval(o));
как используется, но это нестандартно и поддерживается только Firefox .
Я сделал что-то вроде, obj = JSON.parse(JSON.stringify(o));
но поставил под сомнение эффективность.
Я также видел рекурсивные функции копирования с различными недостатками.
Я удивлен, что канонического решения не существует.
javascript
object
clone
jschrab
источник
источник
eval()
как правило, плохая идея, потому что многие оптимизаторы движка Javascript вынуждены отключаться при работе с переменными, которые устанавливаются черезeval
. Просто наличиеeval()
в вашем коде может привести к снижению производительности.JSON
метод потеряет все типы Javascript, которые не имеют эквивалента в JSON. Например:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
сгенерирует{a: null, b: null, c: null, g: false}
Ответы:
Родное глубокое клонирование
Он называется «структурированное клонирование», работает экспериментально в Node 11 и более поздних версиях и, надеюсь, появится в браузерах. Смотрите этот ответ для более подробной информации.
Быстрое клонирование с потерей данных - JSON.parse / stringify
Если вы не используете
Date
с, функцией,undefined
,Infinity
, Regexps, карты, наборы, Blobs, списки файлов, ImageDatas разреженных массивов, типизированные массивы или другими сложными типами в пределах вашего объекта, очень простой один вкладыш к глубоким клонировать объект:JSON.parse(JSON.stringify(object))
Видеть ответ Corban для ориентиров.
Надежное клонирование с использованием библиотеки
Поскольку клонирование объектов не является тривиальным (сложные типы, циклические ссылки, функции и т. Д.), Большинство основных библиотек предоставляют функции для клонирования объектов. Не изобретайте колесо - если вы уже используете библиотеку, проверьте, есть ли у нее функция клонирования объектов. Например,
cloneDeep
; можно импортировать отдельно через lodash.clonedeep модуль и, вероятно, является вашим лучшим выбором, если вы еще не используете библиотеку, которая обеспечивает функцию глубокого клонированияangular.copy
jQuery.extend(true, { }, oldObject)
;.clone()
только клоны DOM-элементовES6
Для полноты обратите внимание, что ES6 предлагает два механизма поверхностного копирования:
Object.assign()
и синтаксис распространения . который копирует значения всех перечисляемых собственных свойств из одного объекта в другой. Например:источник
.clone()
, который не является правильным кодом для используя в этом контексте). К сожалению, этот вопрос подвергся стольким пересмотрам, что первоначальная дискуссия уже не очевидна! Пожалуйста, просто следуйте советам Корбана и напишите цикл или скопируйте свойства непосредственно в новый объект, если вам нужна скорость. Или проверьте это сами!Проверьте этот тест: http://jsben.ch/#/bWfk9
В моих предыдущих тестах, где скорость была главной проблемой, я обнаружил
быть самым медленным способом глубокого клонирования объекта (это медленнее, чем jQuery.extend с
deep
установленным флагом true на 10-20%).jQuery.extend работает довольно быстро, когда установлен
deep
флагfalse
(мелкий клон). Это хороший вариант, потому что он включает некоторую дополнительную логику для проверки типа и не копирует неопределенные свойства и т. Д., Но это также немного замедлит работу.Если вы знаете структуру объектов, которые вы пытаетесь клонировать, или можете избежать вложенных массивов, вы можете написать простую
for (var i in obj)
цикл для клонирования вашего объекта при проверке hasOwnProperty, и он будет намного быстрее, чем jQuery.Наконец, если вы пытаетесь клонировать известную структуру объекта в горячем цикле, вы можете получить НАМНОГО БОЛЬШЕ ЭФФЕКТИВНОСТИ, просто вставив процедуру клонирования и вручную создав объект.
for..in
Механизмы трассировки JavaScript не справляются с оптимизацией циклов, и проверка hasOwnProperty также замедлит вас. Ручное клонирование, когда скорость абсолютно необходима.Остерегайтесь использования
JSON.parse(JSON.stringify(obj))
метода дляDate
объектов -JSON.stringify(new Date())
возвращает строковое представление даты в формате ISO, котороеJSON.parse()
не преобразуется обратно вDate
объект. Смотрите этот ответ для более подробной информации .Кроме того, обратите внимание, что, по крайней мере, в Chrome 65, клонирование по-нативному не подходит. Согласно JSPerf, выполнение нативного клонирования путем создания новой функции почти в 800 раз медленнее, чем при использовании JSON.stringify, который невероятно быстр по всем направлениям.
Обновление для ES6
Если вы используете Javascript ES6, попробуйте этот собственный метод для клонирования или поверхностного копирования.
источник
keys
из вашегоobject
, у которого естьfunctions
их значения, потому чтоJSON
он не поддерживает функции.JSON.parse(JSON.stringify(obj))
объектов Date также преобразует дату обратно в UTC в строковом представлении в формате ISO8601 .Предполагая, что у вас есть только переменные, а не какие-либо функции в вашем объекте, вы можете просто использовать:
источник
JSON
он реализован в собственном коде (в большинстве браузеров), это будет значительно быстрее, чем при использовании любого другого решения глубокого копирования на основе javascript, и иногда может быть быстрее, чем метод поверхностного копирования на основе javascript (см. Jsperf.com/cloning). -ан-объект / 79 ).JSON.stringify({key: undefined}) //=> "{}"
Date
объекты, которые хранятся внутри объекта, преобразовав их в строковую форму.Структурированное клонирование
Стандарт HTML включает внутренний структурированный алгоритм клонирования / сериализации который может создавать глубокие клоны объектов. Он по-прежнему ограничен определенными встроенными типами, но в дополнение к нескольким типам, поддерживаемым JSON, он также поддерживает Dates, RegExps, Карты, Наборы, BLOB-объекты, FileLists, ImageDatas, разреженные массивы, Typed Arrays и, возможно, больше в будущем. , Он также сохраняет ссылки в клонированных данных, что позволяет поддерживать циклические и рекурсивные структуры, которые могут вызвать ошибки для JSON.
Поддержка в Node.js: экспериментальная 🙂
v8
Модуль в Node.js в настоящее время (по состоянию узла 11) предоставляет структурированную сериализации API напрямую , но эта функциональность по- прежнему помечены как «экспериментальные», и с учетом изменения или удаления в версиях будущих. Если вы используете совместимую версию, клонирование объекта так же просто, как:Прямая поддержка в браузерах: возможно, в конце концов? 😐
Браузеры в настоящее время не предоставляют прямой интерфейс для алгоритма структурированного клонирования, но глобальная
structuredClone()
функция обсуждалась в whatwg / html # 793 на GitHub . В настоящее время предлагается использовать его для большинства целей так же просто, как:Если это не доставлено, реализации структурированных клонов в браузерах предоставляются только косвенно.
Асинхронный обходной путь: можно использовать. 😕
Более простой способ создания структурированного клона с существующими API-интерфейсами заключается в публикации данных через один порт MessageChannels . Другой порт выдаст
message
событие со структурированным клоном подключенного.data
. К сожалению, прослушивание этих событий обязательно асинхронно, а синхронные альтернативы менее практичны.Пример использования:
Синхронные обходные пути: Ужасно! 🤢
Хороших вариантов синхронного создания структурированных клонов нет. Вот пара непрактичных взломов вместо этого.
history.pushState()
иhistory.replaceState()
оба создают структурированный клон своего первого аргумента и присваивают это значениеhistory.state
. Вы можете использовать это для создания структурированного клона любого объекта, подобного этому:Пример использования:
Показать фрагмент кода
Хотя синхронно, это может быть очень медленно. Это влечет за собой все накладные расходы, связанные с манипулированием историей браузера. Повторный вызов этого метода может привести к тому, что Chrome временно перестанет отвечать на запросы.
Notification
Конструктор создает структурированный клон связанные с ним данными. Он также пытается отобразить уведомление в браузере для пользователя, но это молча завершится ошибкой, если вы не запросили разрешение на уведомление. Если у вас есть разрешение для других целей, мы немедленно закроем созданное нами уведомление.Пример использования:
Показать фрагмент кода
источник
history.pushState()
иhistory.replaceState()
методы как синхронно установитьhistory.state
в структурированном клон первого аргумента. Немного странно, но это работает. Я обновляю свой ответ сейчас.Если бы не было встроенного, вы можете попробовать:
источник
Эффективный способ клонирования (не глубокого клонирования) объекта в одну строку кода
Object.assign
Метод является частью 2015 (ES6) стандарта ECMAScript и делает именно то , что вам нужно.Читать далее...
Polyfill для поддержки старых браузеров:
источник
Код:
Тестовое задание:
источник
var obj = {}
иobj.a = obj
from.constructor
,Date
к примеру. Как будетif
достигнут третий тест, если второйif
тест будет успешным и вызовет возврат функции (с тех порDate != Object && Date != Array
)?Это то, что я использую:
источник
cloneObject({ name: null })
=>{"name":{}}
typeof null > "object"
ноObject.keys(null) > TypeError: Requested keys of a value that is not an object.
измените условие наif(typeof(obj[i])=="object" && obj[i]!=null)
Глубокое копирование по производительности: от лучшего к худшему
Глубокая копия массива строк или чисел (один уровень - без ссылочных указателей):
Когда массив содержит числа и строки - такие функции, как .slice (), .concat (), .splice (), оператор присваивания "=" и функция клона Underscore.js; сделает глубокую копию элементов массива.
Где переназначение имеет самую быструю производительность:
И .slice () имеет лучшую производительность, чем .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
Глубокая копия массива объектов (два или более уровня - ссылочные указатели):
Напишите пользовательскую функцию (имеет более высокую производительность, чем $ .extend () или JSON.parse):
Используйте сторонние утилиты:
Где jQuery $ .extend имеет лучшую производительность:
источник
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
источник
Глубокое копирование объектов в JavaScript (я думаю, лучший и самый простой)
1. Использование JSON.parse (JSON.stringify (object));
2. Использование созданного метода
3. Использование Lo-Dash _.cloneDeep link lodash
4. Использование метода Object.assign ()
НО НЕПРАВИЛЬНО КОГДА
5. Использование Underscore.js _.clone link Underscore.js
НО НЕПРАВИЛЬНО КОГДА
JSBEN.CH Производительность Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd
источник
Object.assign()
не выполняет глубокую копиюlet data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();
он бросает:TypeError: tmp.title.pop is not a function
(конечно, pop () работает нормально, если я простоdo let tmp = data
; но тогда я не могу изменить tmp без влияния на данные)Есть библиотека (называемая «клон») , которая делает это довольно хорошо. Он обеспечивает наиболее полное рекурсивное клонирование / копирование произвольных известных мне объектов. Он также поддерживает циклические ссылки, которые пока не охвачены другими ответами.
Вы также можете найти его на npm . Может использоваться как для браузера, так и для Node.js.
Вот пример того, как его использовать:
Установите его с
или упакуй это с Эндером .
Вы также можете скачать исходный код вручную.
Затем вы можете использовать его в своем исходном коде.
(Отказ от ответственности: я автор библиотеки.)
источник
JSON.parse(JSON.stringify(obj))
?Cloning
Объект всегда был проблемой в JS, но все это было до ES6, ниже я перечисляю различные способы копирования объекта в JavaScript, представьте, что у вас есть объект ниже и вы хотели бы иметь его глубокую копию:Есть несколько способов скопировать этот объект без изменения источника:
1) ES5 +, используя простую функцию, чтобы сделать копию для вас:
2) ES5 +, используя JSON.parse и JSON.stringify.
3) AngularJs:
4) JQuery:
5) Подчеркиваем и загружаем:
Надеюсь, что это поможет ...
источник
Object.assign
это не глубокая копия. Пример:var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"
. Если бы это была глубокая копия,y.a.b
все равно было быc
, но это сейчасd
.Я знаю, что это старый пост, но я подумал, что это может помочь следующему человеку, который спотыкается.
Пока вы не назначаете объект чему-либо, он не сохраняет ссылки в памяти. Таким образом, чтобы создать объект, которым вы хотите поделиться с другими объектами, вам нужно создать фабрику следующим образом:
источник
const defaultFoo = { a: { b: 123 } };
вы можете пойтиconst defaultFoo = () => ({ a: { b: 123 } };
и ваша проблема решена. Тем не менее, это действительно не ответ на вопрос. Это могло бы иметь больше смысла как комментарий к вопросу, а не как полный ответ.Если вы используете его, в библиотеке Underscore.js есть метод clone .
источник
.clone(...)
метод служебной библиотеки . Они есть в каждой крупной библиотеке, а повторяющиеся краткие, не детализированные ответы бесполезны для большинства посетителей, которые не будут использовать эту конкретную библиотеку.Вот версия ответа ConroyP выше, которая работает, даже если конструктор имеет обязательные параметры:
Эта функция также доступна в моей библиотеке simpleoo .
Редактировать:
Вот более надежная версия (благодаря Джастину Маккэндлессу теперь она также поддерживает циклические ссылки):
источник
Следующее создает два экземпляра одного и того же объекта. Я нашел это и использую это в настоящее время. Это просто и легко в использовании.
источник
Крокфорд предлагает (и я предпочитаю) использовать эту функцию:
Это кратко, работает, как ожидалось, и вам не нужна библиотека.
РЕДАКТИРОВАТЬ:
Это polyfill для
Object.create
, так что вы также можете использовать это.ПРИМЕЧАНИЕ. Если вы используете некоторые из них, у вас могут возникнуть проблемы с некоторыми итерациями, которые используют
hasOwnProperty
. Потому что,create
создайте новый пустой объект, который наследуетoldObject
. Но это все еще полезно и практично для клонирования объектов.Например, если
oldObject.a = 5;
но:
источник
var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };
... davidshariff.com/blog/javascript-inheritance-patternsУ Lodash есть хороший метод _.cloneDeep (value) :
источник
.clone(...)
метод служебной библиотеки . Они есть в каждой крупной библиотеке, а повторяющиеся краткие, не детализированные ответы бесполезны для большинства посетителей, которые не будут использовать эту конкретную библиотеку._.merge({}, objA)
. Если бы только lodash не мутировал объекты в первую очередь, тогдаclone
функция была бы не нужна.источник
Однострочная копия мелкой копии ( ECMAScript 5-е издание ):
И однострочная копия мелкой копии ( ECMAScript 6th edition , 2015):
источник
Object.keys
пропускает не перечисляемые и унаследованные свойства. Кроме того, он теряет дескрипторы свойств, выполняя прямое присваивание.Просто потому, что я не видел упоминания AngularJS и думал, что люди могут захотеть узнать ...
angular.copy
также предоставляет метод глубокого копирования объектов и массивов.источник
angular.extend({},obj);
jQuery.extend
иangular.extend
оба являются мелкими копиями.angular.copy
это глубокая копия.Кажется, еще нет идеального оператора глубокого клонирования для массивоподобных объектов. Как показано в приведенном ниже коде, клонер jQuery Джона Резига превращает массивы с нечисловыми свойствами в объекты, которые не являются массивами, а клонер JSON RegDwight удаляет нечисловые свойства. Следующие тесты иллюстрируют эти пункты в нескольких браузерах:
источник
У меня есть два хороших ответа в зависимости от того, является ли ваша цель клонировать «простой старый объект JavaScript» или нет.
Предположим также, что вы намереваетесь создать полный клон без ссылок прототипов на исходный объект. Если вас не интересует полный клон, вы можете использовать многие из подпрограмм Object.clone (), представленных в некоторых других ответах (шаблон Крокфорда).
Для простых старых объектов JavaScript надежный и надежный способ клонирования объекта в современных средах выполнения довольно прост:
Обратите внимание, что исходный объект должен быть чистым объектом JSON. То есть все его вложенные свойства должны быть скалярами (например, логическое значение, строка, массив, объект и т. Д.). Любые функции или специальные объекты, такие как RegExp или Date, не будут клонированы.
Это эффективно? Черт возьми, да. Мы перепробовали все виды методов клонирования, и это работает лучше всего. Я уверен, что какой-нибудь ниндзя мог бы придумать более быстрый метод. Но я подозреваю, что мы говорим о предельной прибыли.
Этот подход просто прост и легко реализуем. Оберните его в удобную функцию, и если вам действительно нужно выжать некоторую выгоду, сделайте это позже.
Теперь для непростых объектов JavaScript нет действительно простого ответа. На самом деле, не может быть из-за динамической природы функций JavaScript и состояния внутреннего объекта. Глубокое клонирование структуры JSON с функциями внутри требует повторного создания этих функций и их внутреннего контекста. И у JavaScript просто нет стандартизированного способа сделать это.
Правильный способ сделать это, опять же, через удобный метод, который вы объявляете и повторно используете в своем коде. Удобный метод может быть наделен некоторым пониманием ваших собственных объектов, так что вы можете быть уверены, что правильно воссоздаете график в новом объекте.
Мы написаны по-своему, но лучший общий подход, который я видел, описан здесь:
http://davidwalsh.name/javascript-clone
Это правильная идея. Автор (Дэвид Уолш) прокомментировал клонирование обобщенных функций. Это то, что вы можете выбрать, в зависимости от вашего варианта использования.
Основная идея заключается в том, что вам нужно специально обрабатывать создание ваших функций (или, так сказать, прототипов) для каждого типа. Здесь он предоставил несколько примеров для RegExp и Date.
Этот код не только краткий, но и очень читаемый. Это довольно легко расширить.
Это эффективно? Черт возьми, да. Учитывая, что цель состоит в том, чтобы создать настоящий клон глубокой копии, вам придется пройтись по элементам графа исходного объекта. При таком подходе вы можете настроить, какие дочерние элементы обрабатывать, и как вручную обрабатывать пользовательские типы.
Итак, поехали. Два подхода. Оба эффективны, на мой взгляд.
источник
Как правило, это не самое эффективное решение, но оно делает то, что мне нужно. Простые тестовые случаи ниже ...
Тест циклического массива ...
Функциональный тест ...
источник
AngularJS
Ну, если вы используете угловой, вы могли бы сделать это тоже
источник
Я не согласен с ответом с наибольшим количеством голосов здесь . Рекурсивный Deep Clone это гораздо быстрее , чем JSON.parse (JSON.stringify (OBJ)) подход упоминается.
А вот функция для быстрого ознакомления:
источник
if(o instanceof Date) return new Date(o.valueOf());
после проверки на null `источник
Только тогда, когда вы можете использовать ECMAScript 6 или транспортеры .
Особенности:
Код:
источник
Вот всеобъемлющий метод clone (), который может клонировать любой объект JavaScript. Он обрабатывает почти все случаи:
источник