Каков наиболее эффективный способ клонирования объекта JavaScript? Я видел obj = eval(uneval(o));
, но который нестандартен и поддерживается только Firefox.
Я ' вы делали такие вещи, как obj = JSON.parse(JSON.stringify(o));
, но сомневаетесь в эффективности.
Я также видел рекурсивные функции копирования с различными недостатками.
Я удивлен, что канонического решения не существует.
Каков наиболее эффективный способ глубокого клонирования объекта в JavaScript?
Ответ 1
Примечание к 2019 году: изначально это был ответ на другой ответ, а не правильный ответ на этот вопрос. Не знаю, почему он был выбран как правильный ответ. Но так как откаты набрали обороты, и это, безусловно, ответ № 1 на этот вопрос, он суммирует решения в виде ответа вики.
Родное глубокое клонирование
Он называется "структурированное клонирование", работает экспериментально в Node 11 и более поздних версиях и, надеюсь, появится в браузерах. Смотрите этот ответ для более подробной информации.
Быстрое клонирование с потерей данных - JSON.parse/stringify
Если вы не используете Date
s, функции, undefined
, Infinity
, RegExps, Карты, Наборы, BLOB-объекты, FileLists, ImageDatas, разреженные массивы, типизированные массивы или другие сложные типы в вашем объекте, очень простой вкладыш для глубокого клонирования объекта:
JSON.parse(JSON.stringify(object))
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
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()
Ответ 2
Оформить заказ на этот тест: http://jsben.ch/#/bWfk9
В моих предыдущих тестах, где скорость была главной проблемой, я обнаружил
JSON.parse(JSON.stringify(obj))
быть самым медленным способом глубокого клонирования объекта (это медленнее, чем jQuery.extend с deep
флагом, установленным в true на 10-20%).
jQuery.extend довольно быстро работает, когда для флага deep
установлено значение false
(мелкий клон). Это хороший вариант, потому что он включает некоторую дополнительную логику для проверки типа и не копирует неопределенные свойства и т.д., Но это также немного замедлит вас.
Если вы знаете структуру объектов, которые вы пытаетесь клонировать, или можете избежать вложенных массивов, вы можете написать простой цикл for (var я in obj)
для клонирования вашего объекта при проверке hasOwnProperty, и он будет намного быстрее, чем jQuery.
Наконец, если вы пытаетесь клонировать известную структуру объекта в горячем цикле, вы можете получить НАМНОГО БОЛЬШЕ ЭФФЕКТИВНОСТИ, просто вставив процедуру клонирования и создав объект вручную.
for..in
трассировки JavaScript не for..in
оптимизацией циклов for..in
а проверка hasOwnProperty также замедляет работу. Ручное клонирование, когда скорость абсолютно необходима.
var clonedObject = {
knownProp: obj.knownProp,
..
}
Остерегайтесь использования JSON.parse(JSON.stringify(obj))
для объектов Date
- JSON.stringify(new Date())
возвращает строковое представление даты в формате ISO, которое JSON.parse()
не преобразует обратно к объекту Date
. Смотрите этот ответ для более подробной информации.
Кроме того, обратите внимание, что, по крайней мере, в Chrome 65, клонирование по-нативному не подходит. Согласно этому JSPerf, выполнение нативного клонирования путем создания новой функции почти в 800 раз медленнее, чем при использовании JSON.stringify, который невероятно быстр по всем направлениям.
Если вы используете Javascript ES6, попробуйте этот собственный метод для клонирования или поверхностного копирования.
Object.assign({}, obj);
Ответ 3
Предполагая, что у вас есть только переменные, а не какие-либо функции в вашем объекте, вы можете просто использовать:
var newObject = JSON.parse(JSON.stringify(oldObject));
Ответ 4
Структурированное клонирование
Стандарт HTML включает внутренний структурированный алгоритм клонирования/сериализации, который может создавать глубокие клоны объектов. Он по-прежнему ограничен определенными встроенными типами, но в дополнение к нескольким типам, поддерживаемым JSON, он также поддерживает Dates, RegExps, Карты, Наборы, BLOB-объекты, FileLists, ImageDatas, разреженные массивы, Typed Arrays и, возможно, больше в будущем., Он также сохраняет ссылки в клонированных данных, что позволяет поддерживать циклические и рекурсивные структуры, которые могут вызвать ошибки для JSON.
Поддержка в Node.js: экспериментальная 🙂
Модуль v8
в Node.js в настоящее время (по состоянию на Node 11) напрямую предоставляет API структурированной сериализации, но эта функциональность по-прежнему помечена как "экспериментальная" и может быть изменена или удалена в будущих версиях. Если вы используете совместимую версию, клонирование объекта так же просто, как:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
Прямая поддержка в браузерах: возможно, в конце концов? 😐
Браузеры в настоящее время не предоставляют прямой интерфейс для алгоритма структурированного клонирования, но глобальная функция structdClone structuredClone()
обсуждалась в whatwg/html # 793 на GitHub. В настоящее время предлагается использовать его для большинства целей так же просто, как:
const clone = structuredClone(original);
Если это не поставляется, реализации структурированных клонов в браузерах предоставляются только косвенно.
Асинхронный обходной путь: можно использовать. 😕
Более простой способ создания структурированного клона с существующими API-интерфейсами заключается в публикации данных через один порт MessageChannels. Другой порт отправит событие message
со структурированным клоном прикрепленных данных .data
. К сожалению, прослушивание этих событий обязательно асинхронно, а синхронные альтернативы менее практичны.
class StructuredCloner {
constructor() {
this.pendingClones_ = new Map();
this.nextKey_ = 0;
const channel = new MessageChannel();
this.inPort_ = channel.port1;
this.outPort_ = channel.port2;
this.outPort_.onmessage = ({data: {key, value}}) => {
const resolve = this.pendingClones_.get(key);
resolve(value);
this.pendingClones_.delete(key);
};
this.outPort_.start();
}
cloneAsync(value) {
return new Promise(resolve => {
const key = this.nextKey_++;
this.pendingClones_.set(key, resolve);
this.inPort_.postMessage({key, value});
});
}
}
const structuredCloneAsync = window.structuredCloneAsync =
StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
Пример использования:
const main = async () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = await structuredCloneAsync(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
main();
Синхронные обходные пути: Ужасно! 🤢
Нет хороших вариантов для синхронного создания структурированных клонов. Вот пара непрактичных взломов вместо этого.
history.pushState()
и history.replaceState()
создают структурированный клон своего первого аргумента и присваивают это значение history.state
. Вы можете использовать это для создания структурированного клона любого объекта, подобного этому:
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
Пример использования:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
main();
Ответ 5
Если бы не было встроенного, вы могли бы попробовать:
function clone(obj) {
if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
return obj;
if (obj instanceof Date)
var temp = new obj.constructor(); //or new Date(obj);
else
var temp = obj.constructor();
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = clone(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
Ответ 6
Эффективный способ клонирования (не глубокого клонирования) объекта в одной строке кода
Метод Object.assign
является частью стандарта ECMAScript 2015 (ES6) и делает именно то, что вам нужно.
var clone = Object.assign({}, obj);
Метод Object.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;
}
});
}
Ответ 7
код:
// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
if (from == null || typeof from != "object") return from;
if (from.constructor != Object && from.constructor != Array) return from;
if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
from.constructor == String || from.constructor == Number || from.constructor == Boolean)
return new from.constructor(from);
to = to || new from.constructor();
for (var name in from)
{
to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
}
return to;
}
Тест:
var obj =
{
date: new Date(),
func: function(q) { return 1 + q; },
num: 123,
text: "asdasd",
array: [1, "asd"],
regex: new RegExp(/aaa/i),
subobj:
{
num: 234,
text: "asdsaD"
}
}
var clone = extend(obj);
Ответ 8
Это то, что я использую:
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(typeof(obj[i])=="object" && obj[i] != null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
Ответ 9
Глубокая копия по производительности: от лучших до худших
- Переназначение "=" (строковые массивы, числовые массивы - только)
- Slice (строковые массивы, массивы чисел - только)
- Конкатенация (только массивы строк, числовые массивы)
- Пользовательская функция: for-loop или рекурсивная копия
- jQuery $.extend
- JSON.parse (только строковые массивы, массивы чисел, массивы объектов)
- Underscore.js _.clone (только строковые массивы, числовые массивы)
- Lo-Dash _.cloneDeep
Глубоко скопируйте массив строк или чисел (один уровень - без указателей):
Когда массив содержит числа и строки - такие функции, как.slice(),.concat(),.splice(), оператор присваивания "=" и функция clone Underscore.js; сделает глубокую копию элементов массива.
Если переназначение имеет самую высокую производительность:
var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];
И.slice() имеет лучшую производительность, чем.concat(), http://jsperf.com/duplicate-array-slice-vs-concat/3
var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy
Глубоко скопируйте массив объектов (два или более уровней - указатели):
var arr1 = [{object:'a'}, {object:'b'}];
Напишите пользовательскую функцию (имеет более высокую производительность, чем $.extend() или JSON.parse):
function copy(o) {
var out, v, key;
out = Array.isArray(o) ? [] : {};
for (key in o) {
v = o[key];
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
}
return out;
}
copy(arr1);
Используйте сторонние служебные функции:
$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash
Где jQuery $.extend имеет лучшую производительность:
Ответ 10
var clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (var i in this) {
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
}
else
{
newObj[i] = this[i];
}
}
return newObj;
};
Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
Ответ 11
Theres a библиотека (называемая "клон" ), что делает это довольно хорошо. Он обеспечивает наиболее полное рекурсивное клонирование/копирование произвольных объектов, о которых я знаю. Он также поддерживает циркулярные ссылки, которые еще не охватываются другими ответами.
Вы можете найти его на npm. Он может использоваться как для браузера, так и для Node.js.
Вот пример того, как его использовать:
Установите его с помощью
npm install clone
или упакуйте его с помощью Ender.
ender build clone [...]
Вы также можете загрузить исходный код вручную.
Затем вы можете использовать его в своем исходном коде.
var clone = require('clone');
var a = { foo: { bar: 'baz' } }; // inital value of a
var b = clone(a); // clone a -> b
a.foo.bar = 'foo'; // change a
console.log(a); // { foo: { bar: 'foo' } }
console.log(b); // { foo: { bar: 'baz' } }
(Отказ от ответственности: Im автор библиотеки.)
Ответ 12
Я знаю, что это старый пост, но я подумал, что это может помочь кому-то, кто спотыкается.
Пока вы не присваиваете объект чему-либо, он не поддерживает ссылку в памяти. Таким образом, чтобы создать объект, который вы хотите разделить между другими объектами, вам нужно создать factory так:
var a = function(){
return {
father:'zacharias'
};
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
Ответ 13
Cloning
Объект всегда был предметом беспокойства в JS, но все это было до ES6, я перечисляю различные способы копирования объекта в JavaScript ниже, представьте, что у вас есть Объект ниже и вы хотите иметь глубокую копию что:
var obj = {a:1, b:2, c:3, d:4};
Существует несколько способов копирования этого объекта без изменения источника:
1) ES5 +, используя простую функцию для копирования:
function deepCopyObj(obj) {
if (null == obj || "object" != typeof obj) return obj;
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = cloneSO(obj[i]);
}
return copy;
}
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 this object.");
}
2) ES5 +, используя JSON.parse и JSON.stringify.
var deepCopyObj = JSON.parse(JSON.stringify(obj));
3) AngularJs:
var deepCopyObj = angular.copy(obj);
4) jQuery:
var deepCopyObj = jQuery.extend(true, {}, obj);
5) UnderscoreJs и Loadash:
var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy
Надеюсь, что эта помощь...
Ответ 14
Если вы используете его, библиотека Underscore.js имеет clone.
var newObject = _.clone(oldObject);
Ответ 15
Глубокое копирование объектов в JavaScript (я думаю, лучший и самый простой)
1. Использование JSON.parse(JSON.stringify(object));
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
2. Использование созданного метода
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(obj[i] != null && typeof(obj[i])=="object")
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = cloneObject(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
3. Использование Lo-Dash _.cloneDeep link lodash
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
4. Использование метода Object.assign()
var obj = {
a: 1,
b: 2
}
var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }
НО НЕПРАВИЛЬНО КОГДА
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.
5. Использование Underscore.js _.clone link Underscore.js
var obj = {
a: 1,
b: 2
}
var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }
НО НЕПРАВИЛЬНО КОГДА
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)
JSBEN.CH Производительность Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd
Ответ 16
Здесь приведенная выше версия ConroyP, которая работает, даже если у конструктора требуются параметры:
//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
object_create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
function deepCopy(obj) {
if(obj == null || typeof(obj) !== 'object'){
return obj;
}
//make sure the returned object has the same prototype as the original
var ret = object_create(obj.constructor.prototype);
for(var key in obj){
ret[key] = deepCopy(obj[key]);
}
return ret;
}
Эта функция также доступна в моей библиотеке simpleoo.
Edit:
Здесь более надежная версия (благодаря Джастину МакКэндлесу, теперь это поддерживает циклические ссылки):
/**
* Deep copy an object (make copies of all its object properties, sub-properties, etc.)
* An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
* that doesn't break if the constructor has required parameters
*
* It also borrows some code from http://stackoverflow.com/a/11621004/560114
*/
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
if(src === null || typeof(src) !== 'object'){
return src;
}
//Honor native/custom clone methods
if(typeof src.clone == 'function'){
return src.clone(true);
}
//Special cases:
//Date
if(src instanceof Date){
return new Date(src.getTime());
}
//RegExp
if(src instanceof RegExp){
return new RegExp(src);
}
//DOM Element
if(src.nodeType && typeof src.cloneNode == 'function'){
return src.cloneNode(true);
}
// Initialize the visited objects arrays if needed.
// This is used to detect cyclic references.
if (_visited === undefined){
_visited = [];
_copiesVisited = [];
}
// Check if this object has already been visited
var i, len = _visited.length;
for (i = 0; i < len; i++) {
// If so, get the copy we already made
if (src === _visited[i]) {
return _copiesVisited[i];
}
}
//Array
if (Object.prototype.toString.call(src) == '[object Array]') {
//[].slice() by itself would soft clone
var ret = src.slice();
//add it to the visited array
_visited.push(src);
_copiesVisited.push(ret);
var i = ret.length;
while (i--) {
ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
}
return ret;
}
//If we've reached here, we have a regular object
//make sure the returned object has the same prototype as the original
var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
if (!proto) {
proto = src.constructor.prototype; //this line would probably only be reached by very old browsers
}
var dest = object_create(proto);
//add this object to the visited array
_visited.push(src);
_copiesVisited.push(dest);
for (var key in src) {
//Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
//For an example of how this could be modified to do so, see the singleMixin() function
dest[key] = deepCopy(src[key], _visited, _copiesVisited);
}
return dest;
}
//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
object_create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
Ответ 17
Следующее создает два экземпляра одного и того же объекта. Я нашел его и использую его в настоящее время. Это простой и простой в использовании.
var objToCreate = JSON.parse(JSON.stringify(cloneThis));
Ответ 18
Lodash имеет хороший метод _. cloneDeep (значение):
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
Ответ 19
function clone(obj)
{ var clone = {};
clone.prototype = obj.prototype;
for (property in obj) clone[property] = obj[property];
return clone;
}
Ответ 20
Крокфорд предлагает (и я предпочитаю) использовать эту функцию:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var newObject = object(oldObject);
Это краткое, работает так, как ожидалось, и вам не нужна библиотека.
EDIT:
Это polyfill для Object.create
, поэтому вы также можете использовать это.
var newObject = Object.create(oldObject);
ПРИМЕЧАНИЕ.. Если вы используете некоторые из них, у вас могут быть проблемы с некоторой итерацией, которые используют hasOwnProperty
. Потому что create
создает новый пустой объект, который наследует oldObject
. Но это все еще полезно и практично для клонирования объектов.
Например, если oldObject.a = 5;
newObject.a; // is 5
а
oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
Ответ 21
Однострочная копия мелкой копии (ECMAScript 5-е издание):
var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
Однострочная и мелкая копия (ECMAScript 6th edition, 2015):
var origin = { foo : {} };
var copy = Object.assign({}, origin);
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
Ответ 22
Просто потому, что я не видел AngularJS и думал, что люди захотят узнать...
angular.copy
также предоставляет метод глубокого копирования объектов и массивов.
Ответ 23
Кажется, что нет идеального оператора глубокого клонирования для объектов типа массива. Как видно из приведенного ниже кода, клонирующий JQuery John Resig превращает массивы с нечисловыми свойствами в объекты, которые не являются массивами, а клонирование RegDwight JSON отбрасывает нечисловые свойства. Следующие тесты иллюстрируют эти моменты в нескольких браузерах:
function jQueryClone(obj) {
return jQuery.extend(true, {}, obj)
}
function JSONClone(obj) {
return JSON.parse(JSON.stringify(obj))
}
var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);
alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
"\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
"\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
"\nAnd what are the JSONClone names? " + JSONCopy.names)
Ответ 24
У меня есть два хороших ответа в зависимости от того, должна ли ваша цель клонировать "простой старый объект JavaScript" или нет.
Предположим также, что вы намерены создать полный клон без ссылок на прототипы обратно к исходному объекту. Если вас не интересует полный клон, вы можете использовать многие из подпрограмм Object.clone(), предоставленные в некоторых других ответах (шаблон Crockford).
Для простых старых объектов JavaScript проверенный и правдивый способ клонирования объекта в современных условиях выполнения довольно просто:
var clone = JSON.parse(JSON.stringify(obj));
Обратите внимание, что исходный объект должен быть чистым объектом JSON. Это означает, что все его вложенные свойства должны быть скалярами (например, логическими, строковыми, массивными, объектными и т.д.). Любые функции или специальные объекты, такие как RegExp или Date, не будут клонированы.
Это эффективно? Черт возьми. Мы пробовали все виды методов клонирования, и это работает лучше всего. Я уверен, что какой-нибудь ниндзя может вызвать более быстрый метод. Но я подозреваю, что мы говорим о предельных выигрышах.
Этот подход прост и прост в реализации. Оберните его в функцию удобства, и если вам действительно нужно выжать некоторый выигрыш, пойдите в более позднее время.
Теперь для не-простых объектов JavaScript нет простого ответа. На самом деле не может быть из-за динамического характера функций JavaScript и состояния внутреннего объекта. Глубокое клонирование структуры JSON с функциями внутри требует, чтобы вы воссоздали эти функции и их внутренний контекст. И JavaScript просто не имеет стандартного способа сделать это.
Правильный способ сделать это, опять же, с помощью метода удобства, который вы декларируете и повторно используете в своем коде. Метод удобства может быть наделен некоторым пониманием ваших собственных объектов, поэтому вы можете правильно воссоздать график в новом объекте.
Мы пишем наши собственные, но лучший общий подход, который я видел, рассматривается здесь:
http://davidwalsh.name/javascript-clone
Это правильная идея. Автор (Дэвид Уолш) прокомментировал клонирование обобщенных функций. Это то, что вы, возможно, захотите сделать, в зависимости от вашего варианта использования.
Основная идея заключается в том, что вам нужно специально обрабатывать создание ваших функций (или прототипных классов, если можно так выразиться) на основе каждого типа. Здесь он привел несколько примеров для RegExp и Date.
Это не только код, но и очень читаемый. Это довольно легко расширить.
Является ли это эффективным? Черт возьми. Учитывая, что цель состоит в том, чтобы создать настоящий клон с глубокой копией, вам придется ходить по членам графа исходного объекта. При таком подходе вы можете точно настроить, какие дочерние элементы обрабатывать и как вручную обрабатывать настраиваемые типы.
Итак, вы идете. Два подхода. На мой взгляд, обе эффективны.
Ответ 25
В Prototype вы сделаете что-то вроде
newObject = Object.clone(myObject);
Прототипная документация отмечает, что это делает мелкую копию.
Ответ 26
Это не самое эффективное решение, но оно делает то, что мне нужно. Простые тестовые примеры ниже...
function clone(obj, clones) {
// Makes a deep copy of 'obj'. Handles cyclic structures by
// tracking cloned obj in the 'clones' parameter. Functions
// are included, but not cloned. Functions members are cloned.
var new_obj,
already_cloned,
t = typeof obj,
i = 0,
l,
pair;
clones = clones || [];
if (obj === null) {
return obj;
}
if (t === "object" || t === "function") {
// check to see if we've already cloned obj
for (i = 0, l = clones.length; i < l; i++) {
pair = clones[i];
if (pair[0] === obj) {
already_cloned = pair[1];
break;
}
}
if (already_cloned) {
return already_cloned;
} else {
if (t === "object") { // create new object
new_obj = new obj.constructor();
} else { // Just use functions as is
new_obj = obj;
}
clones.push([obj, new_obj]); // keep track of objects we've cloned
for (key in obj) { // clone object members
if (obj.hasOwnProperty(key)) {
new_obj[key] = clone(obj[key], clones);
}
}
}
}
return new_obj || obj;
}
Тест циклического массива...
a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true
Функциональный тест...
f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
Ответ 27
AngularJS
Хорошо, если вы используете angular, вы тоже можете это сделать
var newObject = angular.copy(oldObject);
Ответ 28
// obj target object, vals source object
var setVals = function (obj, vals) {
if (obj && vals) {
for (var x in vals) {
if (vals.hasOwnProperty(x)) {
if (obj[x] && typeof vals[x] === 'object') {
obj[x] = setVals(obj[x], vals[x]);
} else {
obj[x] = vals[x];
}
}
}
}
return obj;
};
Ответ 29
Я не согласен с ответом с наибольшим количеством голосов здесь. Рекурсивный глубокий клон быстрее , чем упомянутый подход JSON.parse(JSON.stringify(obj)).
- Jsperf выделяет здесь номер один: https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5
- Jsben из вышеприведенного ответа, чтобы показать, что рекурсивный глубокий клон превосходит всех остальных: http://jsben.ch/13YKQ
И здесь функция для быстрой справки:
function cloneDeep (o) {
let newO
let i
if (typeof o !== 'object') return o
if (!o) return o
if (Object.prototype.toString.apply(o) === '[object Array]') {
newO = []
for (i = 0; i < o.length; i += 1) {
newO[i] = cloneDeep(o[i])
}
return newO
}
newO = {}
for (i in o) {
if (o.hasOwnProperty(i)) {
newO[i] = cloneDeep(o[i])
}
}
return newO
}
Ответ 30
Для людей, которые хотят использовать версию JSON.parse(JSON.stringify(obj))
, но без потери объектов Date, вы можете использовать второй аргумент метода parse
, чтобы преобразовать строки в Date:
function clone(obj) {
var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
return JSON.parse(JSON.stringify(x), function(k, v) {
if (typeof v === 'string' && regExp.test(v))
return new Date(v);
return v;
});
}