Jak poprawnie sklonować obiekt JavaScript?
Mam obiekt, x
. Chciałbym skopiować go jako obiekt y
, tak aby zmiany na y
nie zmieniały x
. Zdałem sobie sprawę, że kopiowanie obiektów pochodzących z wbudowanych obiektów JavaScript spowoduje dodatkowe, niechciane właściwości. To nie jest problem, ponieważ kopiuję jeden z moich własnych, dosłownie skonstruowanych obiektów.
Jak poprawnie sklonować obiekt JavaScript?
30 answers
Aby to zrobić dla dowolnego obiektu w JavaScript nie będzie proste lub proste. Napotkasz problem błędnego pobierania atrybutów z prototypu obiektu, które powinny zostać pozostawione w prototypie, a nie skopiowane do nowej instancji. Jeśli na przykład dodajesz metodę clone
do Object.prototype
, Jak przedstawiają niektóre odpowiedzi, będziesz musiał jawnie pominąć ten atrybut. Ale co jeśli są inne dodatkowe metody dodane do Object.prototype
, lub inne prototypy pośrednie, których nie o czym? W takim przypadku skopiujesz atrybuty, których nie powinieneś, więc musisz wykryć nieprzewidziane, nielokalne atrybuty za pomocąhasOwnProperty
metoda.
Oprócz niewyliczalnych atrybutów napotkasz trudniejszy problem podczas kopiowania obiektów, które mają ukryte właściwości. Na przykład prototype
jest ukrytą właściwością funkcji. Ponadto, do prototypu obiektu odnosi się atrybut __proto__
, który jest również ukryty i nie będzie kopiowany przez iterację pętli for / in nad atrybutami obiektu źródłowego. Myślę, że __proto__
może być specyficzne dla interpretera JavaScript Firefoksa i może to być coś innego w innych przeglądarkach, ale masz obraz. Nie wszystko da się wyliczyć. Możesz skopiować Ukryty atrybut, jeśli znasz jego nazwę, ale nie wiem, jak go odkryć automatycznie.
Kolejnym problemem w poszukiwaniu eleganckiego rozwiązania jest problem prawidłowego ustawienia dziedziczenia prototypu. Jeśli prototyp Twojego obiektu źródłowego jest Object
, wtedy po prostu utworzenie nowego obiektu ogólnego za pomocą {}
zadziała, ale jeśli prototyp źródłowy jest potomkiem Object
, to będzie brakować dodatkowych elementów z tego prototypu, które pominąłeś za pomocą filtra hasOwnProperty
, lub które były w prototypie, ale nie były wyliczalne w pierwszej kolejności. Jednym z rozwiązań może być wywołanie właściwości constructor
obiektu źródłowego, aby uzyskać początkową kopię obiektu, a następnie skopiować atrybuty, ale nadal nie otrzymasz atrybuty niewyliczalne. Na przykład, a Date
obiekt przechowuje swoje dane jako ukryty członek:
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);
Ciąg daty dla d1
będzie 5 sekund za ciągiem d2
. Sposobem na uczynienie jednej Date
tym samym co drugiej jest wywołanie metody setTime
, ale jest to specyficzne dla klasy Date
. Nie sądzę, że istnieje kuloodporne ogólne rozwiązanie tego problemu, choć byłbym szczęśliwy, że się mylę!
Kiedy musiałem wdrożyć ogólne Głębokie kopiowanie skończyło się kompromitując zakładając, że wystarczy skopiować zwykły Object
, Array
, Date
, String
, Number
, lub Boolean
. Ostatnie 3 typy są niezmienne, więc mógłbym wykonać płytką kopię i nie martwić się o to, że się zmieni. Ponadto założyłem, że dowolne elementy zawarte w Object
lub Array
będą również jednym z 6 prostych typów na tej liście. Można to osiągnąć za pomocą następującego kodu:
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.");
}
Powyższa funkcja będzie działać odpowiednio dla 6 prostych typów, o których wspomniałem, o ile dane w obiektach i tablicach tworzą strukturę drzewiastą. Oznacza to, że w obiekcie nie ma więcej niż jednego odniesienia do tych samych danych. Na przykład:
// 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;
Nie będzie w stanie obsłużyć żadnego obiektu JavaScript, ale może być wystarczający do wielu celów, o ile nie założysz, że będzie działał tylko na cokolwiek rzucisz na niego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-14 15:12:54
Z jQuery możesz skopiować z extend:
var copiedObject = jQuery.extend({}, originalObject)
Kolejne zmiany w copiedObject nie będą miały wpływu na originalObject i vice versa.
Lub zrobić głęboką kopię :
var copiedObject = jQuery.extend(true, {}, originalObject)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 13:28:04
Jeśli nie używasz funkcji w swoim obiekcie, bardzo prosta jedna linijka może być następująca:
var cloneOfA = JSON.parse(JSON.stringify(a));
To działa dla wszystkich rodzajów obiektów zawierających obiekty, tablice, łańcuchy znaków, wartości logiczne i liczby.
Zobacz także Ten artykuł o strukturalnym algorytmie klonowania przeglądarek , który jest używany podczas wysyłania wiadomości do i od pracownika. Zawiera również funkcję głębokiego klonowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-26 14:44:29
W ECMAScript 6 znajduje się Obiekt.Przypisz metodę , która kopiuje wartości wszystkich możliwych do wyliczenia własnych właściwości z jednego obiektu do drugiego. Na przykład:
var x = {myProp: "value"};
var y = Object.assign({}, x);
Ale należy pamiętać, że zagnieżdżone obiekty są nadal kopiowane jako odniesienia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-21 11:14:00
Per MDN :
- Jeśli chcesz skopiować płytkę, użyj
Object.assign({}, a)
- do kopiowania" głębokiego " użyj
JSON.parse(JSON.stringify(a))
Nie ma potrzeby korzystania z zewnętrznych bibliotek, ale najpierw należy sprawdzić zgodność przeglądarki .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-08 07:42:24
Istnieje wiele odpowiedzi, ale żadna nie wspomina o obiekcie.Utwórz z ECMAScript 5, który co prawda nie daje dokładnej kopii, ale ustawia źródło jako prototyp nowego obiektu.
Nie jest to więc dokładna odpowiedź na pytanie, ale rozwiązanie jednoliniowe, a przez to eleganckie. I działa najlepiej w 2 Przypadkach:- gdzie takie dziedziczenie jest przydatne (duh!)
- gdzie obiekt źródłowy nie zostanie zmodyfikowany, dzięki czemu relacja między 2 obiekty nie będące wydaniem.
Przykład:
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
Dlaczego uważam to rozwiązanie za lepsze? Jest natywny, więc nie ma pętli, nie ma rekurencji. Jednak starsze przeglądarki będą potrzebować polyfill.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-19 15:17:52
Elegancki sposób klonowania obiektu Javascript w jednej linii kodu
Metoda Object.assign
jest częścią standardu ECMAScript 2015 (ES6) i robi dokładnie to, czego potrzebujesz.
var clone = Object.assign({}, obj);
Obiekt.metoda assign () służy do kopiowania wartości wszystkich wyliczalnych właściwości własnych z jednego lub więcej obiektów źródłowych do obiektu docelowego.
The polyfill do obsługi starszych przeglądarek:
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;
}
});
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-15 16:42:26
Jeśli nie masz nic przeciwko płytkiej kopii, podkreśl.biblioteka js posiada metodę clone .
y = _.clone(x);
Lub możesz go rozszerzyć jak
copiedObject = _.extend({},originalObject);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-19 18:47:56
Istnieje kilka problemów z większością rozwiązań w Internecie. Więc postanowiłem zrobić kontynuację, która obejmuje, dlaczego przyjęta odpowiedź nie powinna być akceptowana.
Sytuacja wyjściowa
Chcę głęboko skopiować Javascript Object
ze wszystkimi Jego dziećmi i ich dziećmi i tak dalej. Ale ponieważ nie jestem jakimś normalnym programistą, mój Object
ma normalny properties
, circular structures
a nawet nested objects
.
Więc stwórzmy circular structure
i nested object
najpierw.
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Połączmy wszystko w Object
o nazwie a
.
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Następnie chcemy skopiować a
do zmiennej o nazwie b
i zmutować ją.
var b = a;
b.x = 'b';
b.nested.y = 'b';
Wiesz, co tu się stało, bo jeśli nie, to nawet nie wylądowałbyś na tym wielkim pytaniu.
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"
}
}
Znajdźmy rozwiązanie.
JSON
Pierwszą próbą, jaką próbowałem było użycie JSON
.
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
Nie trać na to zbyt wiele czasu, dostaniesz TypeError: Converting circular structure to JSON
.
Kopia rekurencyjna (akceptowana "odpowiedź")
Przyjrzyjmy się zaakceptowanej odpowiedzi.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.");
}
Wygląda dobrze, heh? Jest to rekurencyjna Kopia obiektu i obsługuje również inne typy, takie jak Date
, ale nie było to wymagane.
var b = cloneSO(a);
b.x = 'b';
b.nested.y = 'b';
Rekurencja i circular structures
nie działają dobrze razem... RangeError: Maximum call stack size exceeded
Rozwiązanie natywne
Po kłótni z moim współpracownikiem, mój szef zapytał nas, co się stało, i znalazł proste rozwiązanie po jakimś googlowaniu. Nazywa się Object.create
.
var b = Object.create(a);
b.x = 'b';
b.nested.y = 'b';
To rozwiązanie zostało dodane do Javascript jakiś czas temu, a nawet obsługuje circular structure
.
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"
}
}
... i widzisz, to nie działa z zagnieżdżoną strukturą wewnątrz.
Polyfill dla roztworu natywnego
W starszej przeglądarce jest polyfill dla Object.create
, tak jak w IE 8. To coś takiego jak polecane przez Mozillę i oczywiście nie jest idealne i skutkuje tym samym problemem co rozwiązanie natywne .
function F() {};
function clonePF(o) {
F.prototype = o;
return new F();
}
var b = clonePF(a);
b.x = 'b';
b.nested.y = 'b';
Umieściłem F
poza zasięgiem, żebyśmy mogli przyjrzeć się temu, co mówi nam {34]}.
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
Ten sam problem co natywne rozwiązanie , ale trochę gorszy wynik.
Lepsze (ale nie idealne) rozwiązanie
Podczas kopania, znalazłem podobne pytanie ( w Javascript, podczas wykonywania głębokiej kopii, jak uniknąć cyklu, ponieważ właściwość jest "to"?) do tego, ale o wiele lepiej rozwiązanie.
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<o.length; i++) {
result[i] = cloneDR(o[i]);
}
} else {
result = {};
for (var prop in o)
if (prop != gdcc)
result[prop] = cloneDR(o[prop]);
else if (set)
result[prop] = cloneDR(cache);
}
if (set) {
o[gdcc] = cache; // reset
} else {
delete o[gdcc]; // unset again
}
return result;
}
var b = cloneDR(a);
b.x = 'b';
b.nested.y = 'b';
I spójrzmy na wyjście...
console.log(a, b);
a --> Object {
x: "a",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "a"
}
}
b --> Object {
x: "b",
circ: Object {
me: Object { ... }
},
nested: Object {
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 --> false
Wymagania są dopasowane, ale nadal istnieją pewne mniejsze problemy, w tym zmiana instance
z nested
i circ
na Object
.
Struktura drzew, które mają wspólny liść, nie zostanie skopiowana, staną się dwoma niezależnymi liśćmi:]}
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
Podsumowanie
Nie jest to jednak zbyt dobre rozwiązanie, ponieważ jest to realne rozwiązanie, które może być używane przez rekurencję i pamięć podręczną.]} deep-Kopia obiektu. Obsługuje prosteproperties
, circular structures
i nested object
, ale spowoduje to bałagan w ich instancji podczas klonowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-25 10:44:05
Szczególnie nieeleganckim rozwiązaniem jest użycie kodowania JSON do tworzenia głębokich kopii obiektów, które nie mają metod członkowskich. Metodologia polega na zakodowaniu obiektu docelowego JSON, a następnie dekodowaniu go, otrzymujesz kopię, której szukasz. Możesz dekodować tyle razy, ile chcesz, aby wykonać tyle kopii, ile potrzebujesz.
Oczywiście, funkcje nie należą do JSON, więc działa to tylko dla obiektów bez metod członkowskich.
Ta metodologia była idealna dla mojego przypadku użycia, ponieważ jestem przechowywanie obiektów blob JSON w magazynie klucz-wartość, a kiedy są eksponowane jako obiekty w JavaScript API, każdy obiekt faktycznie zawiera kopię pierwotnego stanu obiektu, więc możemy obliczyć delta po tym, jak wywołujący zmutował eksponowany obiekt.
var object1 = {key:"value"};
var object2 = object1;
object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);
object2.key = "a change";
console.log(object1);// returns value
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-28 17:24:12
Możesz po prostu użyć właściwości spread, Aby skopiować obiekt bez referencji. Ale uważaj (patrz komentarze), 'kopia' jest tylko na najniższym poziomie obiektu / tablicy. Zagnieżdżone właściwości są nadal referencjami!
Kompletny klon:
let x = {a: 'value1'}
let x2 = {...x}
// => mutate without references:
x2.a = 'value2'
console.log(x.a) // => 'value1'
Klon z odniesieniami na drugim poziomie:
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 faktycznie nie obsługuje głębokich klonów natywnie. Użyj funkcji użytkowej. Na przykład Ramda:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-05 12:30:15
Dla tych, którzy używają AngularJS, istnieje również bezpośrednia metoda klonowania lub rozszerzania obiektów w tej bibliotece.
var destination = angular.copy(source);
Lub
angular.copy(source, destination);
Więcej w angular.Kopia dokumentacja...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-03 19:08:28
Ok, wyobraź sobie, że masz ten obiekt poniżej i chcesz go sklonować:
let obj = {a:1, b:2, c:3}; //ES6
Lub
var obj = {a:1, b:2, c:3}; //ES5
Odpowiedź jest głównie Depened, na którym ECMAscript używasz, w ES6+
, możesz po prostu użyć Object.assign
aby wykonać klon:
let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};
Lub używając operatora spreadu w ten sposób:
let cloned = {...obj}; //new {a:1, b:2, c:3};
Ale jeśli używasz ES5
, możesz użyć kilku metod, ale JSON.stringify
, po prostu upewnij się, że nie używasz dużej ilości danych do skopiowania, ale może to być jedna linia przydatna w wielu przypadki, coś takiego:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-06 22:48:04
Odpowiedź A. Levy ' ego jest prawie kompletna, oto mój mały wkład: istnieje sposób, jak obsługiwać odwołania rekurencyjne, Zobacz ten wiersz
if(this[attr]==this) copy[attr] = copy;
Jeśli obiekt jest elementem XML DOM, musimy użyć cloneNode zamiast
if(this.cloneNode) return this.cloneNode(true);
Zainspirowany wyczerpującym badaniem A. Levy ' ego i podejściem do prototypowania Calvina, oferuję takie rozwiązanie:
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;
}
Zobacz także notatkę Andy 'ego Burke' a w odpowiedziach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-02 20:49:01
Z tego artykułu: Jak kopiować tablice i obiekty w 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;
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-22 22:06:59
W ES-6 można po prostu użyć obiektu.assign(...). Ex:
let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);
Dobre odniesienie jest tutaj: https://googlechrome.github.io/samples/object-assign-es6/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 20:17:11
Oto funkcja, której możesz użyć.
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-23 20:04:41
Możesz sklonować obiekt i usunąć wszelkie odniesienia z poprzedniego za pomocą jednej linii kodu. Po prostu zrób:
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'}
Dla przeglądarek / silników, które obecnie nie obsługują obiektu.Utwórz możesz użyć tego 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();
};
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-16 05:27:01
Nowa odpowiedź na Stare pytanie! Jeśli masz przyjemność używać ECMAScript 2016 (ES6) z składnią rozproszoną , jest to proste.
keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}
Zapewnia to czystą metodę płytkiej kopii obiektu. Wykonanie głębokiej kopii, czyli makign nowej kopii każdej wartości w każdym rekurencyjnie zagnieżdżonym obiekcie, wymaga zastosowania cięższych rozwiązań powyżej.
JavaScript ciągle ewoluuje.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-16 11:34:33
Używając Lodash:
var y = _.clone(x, true);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-13 00:05:07
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)
Rozwiązanie ES6 jeśli chcesz (płytko) sklonować instancję klasy , a nie tylko obiekt Właściwości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-27 12:57:57
Zainteresowani klonowaniem prostych obiektów:
JSON.parse (JSON.stringify(json_original));
Source: Jak skopiować obiekt JavaScript do nowej zmiennej nie przez odniesienie?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:27
Powyższa odpowiedź Jana Turoňa jest bardzo bliska i może być najlepsza w przeglądarce ze względu na problemy ze zgodnością, ale może powodować dziwne problemy z wyliczaniem. Na przykład wykonanie:
for ( var i in someArray ) { ... }
Przypisze metodę clone() do i po iteracji elementów tablicy. Oto adaptacja, która unika wyliczania i działa z węzłem.js:
Object.defineProperty( Object.prototype, "clone", {
value: 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;
}
});
Object.defineProperty( Date.prototype, "clone", {
value: function() {
var copy = new Date();
copy.setTime( this.getTime() );
return copy;
}
});
Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );
Pozwala to uniknąć wyliczenia metody clone (), ponieważ defineproperty () domyślnie wylicza fałsz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-30 06:03:57
Jest to adaptacja kodu A. Levy ' ego do klonowania funkcji i wielokrotnych / cyklicznych odniesień - oznacza to, że jeśli dwie właściwości w drzewie, które jest sklonowane, są odniesieniami do tego samego obiektu, to klonowane drzewo obiektów będzie miało te właściwości wskazujące na jeden i ten sam klon odwołanego obiektu. Rozwiązuje to również przypadek cyklicznych zależności, które, jeśli pozostawione nieobsługiwane, prowadzą do nieskończonej pętli. Złożoność algorytmu wynosi O (n)
function clone(obj){
var clonedObjectsArray = [];
var originalObjectsArray = []; //used to remove the unique ids when finished
var next_objid = 0;
function objectId(obj) {
if (obj == null) return null;
if (obj.__obj_id == undefined){
obj.__obj_id = next_objid++;
originalObjectsArray[obj.__obj_id] = obj;
}
return obj.__obj_id;
}
function cloneRecursive(obj) {
if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") 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; i < obj.length; ++i) {
copy[i] = cloneRecursive(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
if (clonedObjectsArray[objectId(obj)] != undefined)
return clonedObjectsArray[objectId(obj)];
var copy;
if (obj instanceof Function)//Handle Function
copy = function(){return obj.apply(this, arguments);};
else
copy = {};
clonedObjectsArray[objectId(obj)] = copy;
for (var attr in obj)
if (attr != "__obj_id" && obj.hasOwnProperty(attr))
copy[attr] = cloneRecursive(obj[attr]);
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
var cloneObj = cloneRecursive(obj);
//remove the unique ids
for (var i = 0; i < originalObjectsArray.length; i++)
{
delete originalObjectsArray[i].__obj_id;
};
return cloneObj;
}
Niektóre szybkie testy
var auxobj = {
prop1 : "prop1 aux val",
prop2 : ["prop2 item1", "prop2 item2"]
};
var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;
obj.f1 = function (){
this.prop1 = "prop1 val changed by f1";
};
objclone = clone(obj);
//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));
objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));
objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-16 09:23:13
Napisałam własną implementację. Nie wiem czy liczy się jako lepsze rozwiązanie:
/*
a function for deep cloning objects that contains other nested objects and circular structures.
objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
index (z)
|
|
|
|
|
| depth (x)
|_ _ _ _ _ _ _ _ _ _ _ _
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/...../
/................./
/..... /
/ /
/------------------
object length (y) /
*/
Poniżej znajduje się implementacja:
function deepClone(obj) {
var depth = -1;
var arr = [];
return clone(obj, arr, depth);
}
/**
*
* @param obj source object
* @param arr 3D array to store the references to objects
* @param depth depth of the current object relative to the passed 'obj'
* @returns {*}
*/
function clone(obj, arr, depth){
if (typeof obj !== "object") {
return obj;
}
var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'
var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
if(result instanceof Array){
result.length = length;
}
depth++; // depth is increased because we entered an object here
arr[depth] = []; // this is the x-axis, each index here is the depth
arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
// start the depth at current and go down, cyclic structures won't form on depths more than the current one
for(var x = depth; x >= 0; x--){
// loop only if the array at this depth and length already have elements
if(arr[x][length]){
for(var index = 0; index < arr[x][length].length; index++){
if(obj === arr[x][length][index]){
return obj;
}
}
}
}
arr[depth][length].push(obj); // store the object in the array at the current depth and length
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
}
return result;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 16:32:06
Chciałem tylko dodać do wszystkich Object.create
rozwiązań w tym poście, że to nie działa w pożądany sposób z nodejs.
W Firefoksie wynik
var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´
Jest
{test:"test"}
.
W nodejs jest
{}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-03 09:29:51
function clone(src, deep) {
var toString = Object.prototype.toString;
if(!src && typeof src != "object"){
//any non-object ( Boolean, String, Number ), null, undefined, NaN
return src;
}
//Honor native/custom clone methods
if(src.clone && toString.call(src.clone) == "[object Function]"){
return src.clone(deep);
}
//DOM Elements
if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
return src.cloneNode(deep);
}
//Date
if(toString.call(src) == "[object Date]"){
return new Date(src.getTime());
}
//RegExp
if(toString.call(src) == "[object RegExp]"){
return new RegExp(src);
}
//Function
if(toString.call(src) == "[object Function]"){
//Wrap in another method to make sure == is not true;
//Note: Huge performance issue due to closures, comment this :)
return (function(){
src.apply(this, arguments);
});
}
var ret, index;
//Array
if(toString.call(src) == "[object Array]"){
//[].slice(0) would soft clone
ret = src.slice();
if(deep){
index = ret.length;
while(index--){
ret[index] = clone(ret[index], true);
}
}
}
//Object
else {
ret = src.constructor ? new src.constructor() : {};
for (var prop in src) {
ret[prop] = deep
? clone(src[prop], true)
: src[prop];
}
}
return ret;
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-31 20:53:16
Ponieważ mindeavor stwierdził, że obiekt, który ma być sklonowany, jest obiektem "skonstruowanym literalnie", rozwiązaniem może być po prostu wygenerowanie obiektu wiele razy, a nie klonowanie instancji obiektu:
function createMyObject()
{
var myObject =
{
...
};
return myObject;
}
var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:51
Consult http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data dla algorytmu W3C" Safe passing of structured data", przeznaczonego do implementacji przez przeglądarki do przekazywania danych np. webowym pracownikom. Ma jednak pewne ograniczenia, ponieważ nie obsługuje funkcji. Zobacz https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm aby uzyskać więcej informacji, w tym alternatywny algorytm w JS, który cię część drogi tam.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-16 02:56:17
Poniżej jest moją wersją głębokiego klonowania, obejmującą funkcje i z obsługą dla odniesień kołowych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-05 09:24:25