Mam obiektu x
. Chciałbym skopiować go jako obiekt y
, aby zmiany y
nie były modyfikowane x
. Zdałem sobie sprawę, że kopiowanie obiektów pochodzących z wbudowanych obiektów JavaScript spowoduje dodatkowe, niepożądane właściwości. To nie jest problem, ponieważ kopiuję jeden z moich dosłownie skonstruowanych obiektów.
Jak poprawnie sklonować obiekt JavaScript?
javascript
clone
javascript-objects
dźwięcznie
źródło
źródło
mObj=JSON.parse(JSON.stringify(jsonObject));
Object.create(o)
, że robi wszystko, o co prosi autor?var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;
Po zrobieniu tegoy.deep.key
będzie również 2, stąd Object.create NIE MOŻE BYĆ UŻYWANY do klonowania ...Odpowiedzi:
Aby to zrobić dla dowolnego obiektu w JavaScript, nie będzie proste ani jednoznaczne. Będziesz miał problem z błędnym pobieraniem atrybutów z prototypu obiektu, które powinny zostać w prototypie, a nie kopiowane do nowej instancji. Jeśli na przykład dodajesz
clone
metodęObject.prototype
, jak pokazują niektóre odpowiedzi, musisz jawnie pominąć ten atrybut. Ale co będzie, jeśli zostaną dodane inne dodatkowe metodyObject.prototype
lub inne pośrednie prototypy, o których nie wiesz? W takim przypadku skopiujesz atrybuty, których nie powinieneś, więc musisz wykryć nieprzewidziane, nielokalne atrybuty za pomocą tejhasOwnProperty
metody.Oprócz niepoliczalnych atrybutów napotkasz trudniejszy problem podczas próby kopiowania obiektów o ukrytych właściwościach. Na przykład
prototype
jest ukrytą właściwością funkcji. Do prototypu obiektu odwołuje się również atrybut__proto__
, który jest również ukryty i nie będzie kopiowany przez pętlę for / in iterującą atrybuty obiektu źródłowego. Myślę, że__proto__
może być specyficzny dla interpretera JavaScript Firefoksa i może być coś innego w innych przeglądarkach, ale dostajesz obraz. Nie wszystko jest policzalne. Możesz skopiować ukryty atrybut, jeśli znasz jego nazwę, ale nie znam żadnego sposobu na jego automatyczne wykrycie.Kolejną przeszkodą w poszukiwaniu eleganckiego rozwiązania jest problem z prawidłowym skonfigurowaniem dziedziczenia prototypu. Jeśli prototypem obiektu źródłowego jest
Object
, to po prostu{}
będzie działać tworzenie nowego obiektu ogólnego , ale jeśli prototypem źródła jest jakiś potomekObject
, będzie brakowało dodatkowych elementów z tego prototypu, który pominięto za pomocąhasOwnProperty
filtra lub który były w prototypie, ale po pierwsze nie były wymienialne. Jednym z rozwiązań może być wywołanie właściwości obiektu źródłowego wconstructor
celu uzyskania początkowego obiektu kopiowania, a następnie skopiowania atrybutów, ale wtedy nadal nie otrzymasz atrybutów niepoliczalnych. Na przykładDate
obiekt przechowuje swoje dane jako ukryty element członkowski:Ciąg daty
d1
będzie o 5 sekund opóźnionyd2
. Sposobem na uczynienie jednegoDate
takim samym jak drugim jest wywołaniesetTime
metody, ale jest to specyficzne dlaDate
klasy. Nie sądzę, aby istniało ogólne rozwiązanie tego problemu w kuloodporny sposób, choć chętnie bym się mylił!Gdy miałem wdrożyć ogólne realizuje głębokie kopiowanie skończyło się kompromisów przy założeniu, że będę tylko trzeba skopiować zwykły
Object
,Array
,Date
,String
,Number
, lubBoolean
. Ostatnie 3 typy są niezmienne, więc mogłem wykonać płytką kopię i nie martwić się o jej zmianę. Ponadto założyłem, że wszelkie elementy zawarte wObject
lubArray
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:Powyższa funkcja będzie działać poprawnie dla 6 prostych typów, o których wspomniałem, o ile dane w obiektach i tablicach tworzą strukturę drzewa. Oznacza to, że w obiekcie nie ma więcej niż jednego odwołania do tych samych danych. Na przykład:
Nie będzie w stanie obsłużyć żadnego obiektu JavaScript, ale może być wystarczający do wielu celów, o ile nie zakładasz, że będzie on działał tylko do wszystkiego, co w niego rzucisz.
źródło
JSON.parse(JSON.stringify([some object]),[some revirer function])
byłoby rozwiązanie?var cpy = new obj.constructor()
?Jeśli nie używasz
Date
s, funkcji, niezdefiniowanej, regExp lub Infinity w swoim obiekcie, bardzo prosta jedna linijka toJSON.parse(JSON.stringify(object))
:Działa to dla wszelkiego rodzaju obiektów zawierających obiekty, tablice, ciągi znaków, wartości logiczne i liczby.
Zobacz także ten artykuł na temat uporządkowanego algorytmu klonowania przeglądarek, który jest używany podczas wysyłania wiadomości do i od pracownika. Zawiera także funkcję głębokiego klonowania.
źródło
Dzięki jQuery możesz płytko kopiować z rozszerzeniem :
kolejne zmiany
copiedObject
nie będą miały wpływu naoriginalObject
i odwrotnie.Lub zrobić głęboką kopię :
źródło
var copiedObject = jQuery.extend({},originalObject);
jQuery.extend(true, {}, originalObject);
...subsequent changes to the copiedObject will not affect the originalObject, and vice versa...
. Przepraszam, że byłem naprawdę zdezorientowany.W ECMAScript 6 istnieje metoda Object.assign , która kopiuje wartości wszystkich wyliczalnych własnych właściwości z jednego obiektu do drugiego. Na przykład:
Należy jednak pamiętać, że zagnieżdżone obiekty są nadal kopiowane jako odniesienie.
źródło
Object.assign
to jest właściwa droga. Łatwo jest go również wypełnićWedług MDN :
Object.assign({}, a)
JSON.parse(JSON.stringify(a))
Biblioteki zewnętrzne nie są potrzebne, ale najpierw należy sprawdzić zgodność przeglądarki .
źródło
clone
funkcjonalność. zobacz więcej tutaj momentjs.com/docs/#/parsing/moment-cloneIstnieje wiele odpowiedzi, ale żadna nie wspomina o Object.create z ECMAScript 5, który wprawdzie nie daje dokładnej kopii, ale ustawia źródło jako prototyp nowego obiektu.
Dlatego nie jest to dokładna odpowiedź na pytanie, ale jest to rozwiązanie jednorzędowe, a zatem eleganckie. I działa najlepiej w 2 przypadkach:
Przykład:
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ą wymagały wypełniania.
źródło
var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
Object.create
wskazuje na właściwości rodzica za pomocą referencji. Oznacza to, że jeśli wartości właściwości rodzica zmienią się, zmieni się również własność dziecka. Ma to zaskakujące skutki uboczne związane z zagnieżdżonymi tablicami i obiektami, które mogą prowadzić do trudnych do znalezienia błędów w kodzie, jeśli nie są ci one znane: jsbin.com/EKivInO/2 . Sklonowany obiekt jest całkowicie nowym, niezależnym obiektem, który ma takie same właściwości i wartości jak rodzic, ale nie jest połączony z rodzicem.Elegancki sposób klonowania obiektu JavaScript w jednym wierszu kodu
Object.assign
Metoda jest częścią standardu (2015) ES6 ECMAScript i robi dokładnie to, czego potrzebujesz.Czytaj więcej...
Polyfill do obsługi starszych przeglądarek:
źródło
Object.assign
bierze dwa parametry, skorovalue
funkcja w poliolinie zajmuje tylko jeden parametr?arguments
obiektu). Mam problem ze znalezieniemObject()
za pośrednictwem Google ... to typecast, prawda?Istnieje wiele problemów z większością rozwiązań w Internecie. Postanowiłem więc podjąć dalsze działania, w tym wyjaśnić, dlaczego zaakceptowana odpowiedź nie powinna zostać zaakceptowana.
sytuacja początkowa
Chcę głęboko skopiować Javascript
Object
ze wszystkimi jego dziećmi i ich dziećmi i tak dalej. Ale ponieważ nie jestem normalnym programistą, mójObject
ma normalnyproperties
,circular structures
a nawetnested objects
.Stwórzmy więc
circular structure
a anested object
first.Połączmy wszystko w jednym
Object
nazwanyma
.Następnie chcemy skopiować
a
do zmiennej o nazwieb
i zmutować ją.Wiesz, co się tutaj wydarzyło, bo jeśli nie, nie trafiłbyś nawet na to wspaniałe pytanie.
Teraz znajdźmy rozwiązanie.
JSON
Pierwszą próbą, której próbowałem, było użycie
JSON
.Nie marnuj na to zbyt wiele czasu, dostaniesz
TypeError: Converting circular structure to JSON
.Kopiowanie rekurencyjne (zaakceptowana „odpowiedź”)
Rzućmy okiem na przyjętą odpowiedź.
Wygląda dobrze, heh? Jest to rekurencyjna kopia obiektu i obsługuje również inne typy
Date
, ale nie było to wymagane.Rekurencja i
circular structures
nie działa dobrze razem ...RangeError: Maximum call stack size exceeded
natywne rozwiązanie
Po kłótni z moim współpracownikiem mój szef zapytał nas, co się stało, a po pewnym czasie google znalazł proste rozwiązanie . To się nazywa
Object.create
.To rozwiązanie zostało dodane do Javascript jakiś czas temu, a nawet obsługuje
circular structure
.... i widzisz, to nie działało z zagnieżdżoną strukturą wewnątrz.
polyfill do natywnego rozwiązania
W
Object.create
starszej przeglądarce, podobnie jak IE 8., znajduje się polifill . Jest to coś takiego, jak zalecane przez Mozillę i oczywiście nie jest idealne i powoduje ten sam problem co rozwiązanie natywne .Wyłączyłem
F
zakres, abyśmy mogli zobaczyć, coinstanceof
nam mówi.Ten sam problem co rozwiązanie natywne , ale nieco gorszy wynik.
lepsze (ale nie idealne) rozwiązanie
Podczas kopania znalazłem podobne pytanie (w JavaScript, kiedy wykonuję głęboką kopię, jak mogę uniknąć cyklu, ponieważ właściwość jest „tym”? ) Do tego, ale ze znacznie lepszym rozwiązaniem.
I spójrzmy na wynik ...
Wymagania są dopasowane, ale nadal istnieją pewne kwestie, w tym mniejsze zmieniając
instance
odnested
icirc
doObject
.wniosek
Ostatnie rozwiązanie wykorzystujące rekurencję i pamięć podręczną może nie być najlepsze, ale jest to prawdziwa głęboka kopia obiektu. Zajmuje proste
properties
,circular structures
anested object
, ale to będzie bałagan instancji z nich podczas klonowania.jsfiddle
źródło
Jeśli nie masz problemu z płytką kopią, biblioteka underscore.js ma metodę klonowania .
lub możesz to przedłużyć
źródło
OK, wyobraź sobie, że masz ten obiekt poniżej i chcesz go sklonować:
lub
odpowiedź zależy głównie od używanego skryptu ECMA , w
ES6+
którym można po prostu użyćObject.assign
do wykonania klonowania:lub za pomocą operatora spreadu w następujący sposób:
Ale jeśli używasz
ES5
, możesz użyć kilku metod, aleJSON.stringify
upewnij się, że nie używasz dużej części danych do skopiowania, ale w wielu przypadkach może to być jeden wiersz przydatny, coś takiego:źródło
big chunk of data
by się równało? 100kb? 100 MB? Dzięki!Object.assign
wykonuje płytką kopię (podobnie jak spread, @Alizera)Jednym szczególnie nieelegacyjnym rozwiązaniem jest użycie kodowania JSON do tworzenia głębokich kopii obiektów, które nie mają metod składowych. Metodologia polega na kodowaniu JSON obiektu docelowego, a następnie poprzez dekodowanie 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 składowych.
Ta metodologia była idealna dla mojego przypadku użycia, ponieważ przechowuję obiekty BLS JSON w magazynie kluczy i wartości, a gdy są one ujawniane jako obiekty w JavaScript API, każdy obiekt faktycznie zawiera kopię oryginalnego stanu obiektu, więc może obliczyć deltę po tym, jak osoba wywołująca zmutuje odsłonięty obiekt.
źródło
{ 'foo': function() { return 1; } }
jest dosłownie skonstruowanym obiektem.Możesz po prostu użyć właściwości spread do skopiowania obiektu bez odwołań. Ale bądź ostrożny (patrz komentarze), „kopia” jest tylko na najniższym poziomie obiektu / tablicy. Zagnieżdżone właściwości są nadal referencjami!
Kompletny klon:
Klonuj z referencjami na drugim poziomie:
JavaScript faktycznie nie obsługuje natywnie głębokich klonów. Użyj funkcji narzędzia. Na przykład Ramda:
źródło
const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Dla osób korzystających z AngularJS istnieje również bezpośrednia metoda klonowania lub rozszerzania obiektów w tej bibliotece.
lub
Więcej w dokumentacji angular.copy ...
źródło
Oto funkcja, której możesz użyć.
źródło
new
. Przyjęta odpowiedź nie.Odpowiedź A.Levy jest prawie kompletna, oto mój mały wkład: istnieje sposób, w jaki sposób obsługiwać rekurencyjne referencje , zobacz ten wiersz
if(this[attr]==this) copy[attr] = copy;
Jeśli obiektem jest element XML DOM, musimy zamiast tego użyć cloneNode
if(this.cloneNode) return this.cloneNode(true);
Zainspirowany wyczerpującymi badaniami A.Levy'ego i podejściem do prototypowania Calvina, oferuję to rozwiązanie:
Zobacz także notatkę Andy'ego Burke'a w odpowiedziach.
źródło
Date.prototype.clone = function() {return new Date(+this)};
Z tego artykułu: Jak skopiować tablice i obiekty w JavaScript autorstwa Briana Huismana:
źródło
var copiedObj = Object.create(obj);
jest świetny sposób?W ES-6 możesz po prostu użyć Object.assign (...). Dawny:
Dobry odnośnik znajduje się tutaj: https://googlechrome.github.io/samples/object-assign-es6/
źródło
let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";
)obj
właściwości są rzeczywiście klonowane. Wartości właściwości, które same są obiektami, nie będą klonowane.W ECMAScript 2018
Pamiętaj, że zagnieżdżone obiekty są nadal kopiowane jako odniesienie.
źródło
const objDeepClone = JSON.parse(JSON.stringify(obj));
Korzystanie z Lodash:
źródło
_.cloneDeep(x)
ponieważ zasadniczo jest to to samo, co powyżej, ale czyta lepiej.Zainteresowany klonowaniem prostych obiektów:
JSON.parse(JSON.stringify(json_original));
Źródło: Jak skopiować obiekt JavaScript do nowej zmiennej NOT przez odniesienie?
źródło
Możesz sklonować obiekt i usunąć dowolne odniesienie z poprzedniego, używając jednego wiersza kodu. Po prostu wykonaj:
W przeglądarkach / silnikach, które obecnie nie obsługują Object.create, możesz użyć tego wypełniania:
źródło
Object.create(...)
wydaje się zdecydowanie właściwą drogą.Object.hasOwnProperty
? W ten sposób ludzie wiedzą, jak zapobiec przeszukiwaniu prototypowego łącza.text
członkowski w obj2. Nie tworzysz kopii, po prostu odkładasz na łańcuch prototypów, gdy nie ma elementu na obj2.Nowa odpowiedź na stare pytanie! Jeśli masz przyjemność korzystać z ECMAScript 2016 (ES6) z Spread Syntax , jest to łatwe.
Zapewnia to czystą metodę płytkiej kopii obiektu. Wykonywanie głębokiej kopii, co oznacza tworzenie nowej kopii każdej wartości w każdym rekurencyjnie zagnieżdżonym obiekcie, wymaga jednego z cięższych rozwiązań powyżej.
JavaScript ewoluuje.
źródło
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
Rozwiązanie ES6, jeśli chcesz (płytko) sklonować instancję klasy, a nie tylko obiekt właściwości.
źródło
let cloned = Object.assign({}, obj)
?Myślę, że jest prosta i działająca odpowiedź. W głębokim kopiowaniu istnieją dwie obawy:
Myślę więc, że jednym prostym rozwiązaniem będzie najpierw serializacja i deserializacja, a następnie przypisanie jej do funkcji kopiowania.
Chociaż na to pytanie jest wiele odpowiedzi, mam nadzieję, że to również pomaga.
źródło
cloneDeep
.Aby uzyskać głębokie kopiowanie i klonowanie, JSON.stringify, a następnie JSON.parse obiektu:
źródło
Jest to adaptacja kodu A. Levy'ego do obsługi klonowania funkcji i wielokrotnych / cyklicznych odwołań - oznacza to, że jeśli dwie właściwości w klonowanym drzewie są odwołaniami do tego samego obiektu, klonowane drzewo obiektów będzie miało te właściwości wskazują na jeden i ten sam klon odnośnego obiektu. To rozwiązuje również przypadek cyklicznych zależności, które, jeśli nie są obsługiwane, prowadzą do nieskończonej pętli. Złożoność algorytmu wynosi O (n)
Kilka szybkich testów
źródło
Chciałem tylko dodać do wszystkich
Object.create
rozwiązań w tym poście, że nie działa to w pożądany sposób z nodejs.W Firefox wynik
jest
{test:"test"}
.W nodejs jest
źródło
Object.hasOwnProperty
aby sprawdzić, czy jesteś właścicielem tablicy, czy nie. Tak, powoduje to dodatkową złożoność w przypadku dziedziczenia prototypowego.źródło
if(!src && typeof src != "object"){
. Myślę, że tak||
nie powinno być&&
.Ponieważ mindeavour stwierdził, że klonowany obiekt jest obiektem „zbudowanym dosłownie”, rozwiązaniem może być po prostu wielokrotne wygenerowanie obiektu zamiast klonowania instancji obiektu:
źródło
Napisałem własną implementację. Nie jestem pewien, czy liczy się to jako lepsze rozwiązanie:
Poniżej przedstawiono wdrożenie:
źródło