Jaki jest najskuteczniejszy sposób klonowania obiektu JavaScript? Widziałem, że jestem obj = eval(uneval(o));
używany, ale to niestandardowe i obsługiwane tylko przez Firefox .
Robiłem takie rzeczy, obj = JSON.parse(JSON.stringify(o));
ale kwestionowałem efektywność.
Widziałem także funkcje kopiowania rekurencyjnego z różnymi wadami.
Dziwi mnie, że nie ma kanonicznego rozwiązania.
javascript
object
clone
jschrab
źródło
źródło
eval()
jest ogólnie złym pomysłem, ponieważ wiele optymalizatorów silnika Javascript musi się wyłączyć, gdy mamy do czynienia ze zmiennymi ustawionymi za pomocąeval
. Sameval()
kod może prowadzić do gorszej wydajności.JSON
metoda straci wszystkie typy Javascript, które nie mają odpowiednika w JSON. Na przykład:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
wygeneruje{a: null, b: null, c: null, g: false}
Odpowiedzi:
Natywne głębokie klonowanie
Nazywa się to „klonowaniem strukturalnym”, działa eksperymentalnie w węźle 11 i późniejszych i mam nadzieję, że wyląduje w przeglądarkach. Zobacz tę odpowiedź, aby uzyskać więcej informacji.
Szybkie klonowanie z utratą danych - JSON.parse / stringify
Jeśli nie używać
Date
s, funkcji,undefined
,Infinity
, wyrażenia regularne, mapy, zestawy, Blobs, FileLists, ImageDatas, rzadki tablic, wpisane tablic lub innych typów złożonych w swoim obiekcie, bardzo prosta wkładka do głębokiego klonu przedmiotem jest:JSON.parse(JSON.stringify(object))
Zobacz odpowiedź Corban jest dla odniesienia.
Niezawodne klonowanie przy użyciu biblioteki
Ponieważ klonowanie obiektów nie jest trywialne (typy złożone, odwołania kołowe, funkcja itp.), Większość głównych bibliotek zapewnia funkcję klonowania obiektów. Nie wymyślaj na nowo koła - jeśli już korzystasz z biblioteki, sprawdź, czy ma ona funkcję klonowania obiektów. Na przykład,
cloneDeep
; można zaimportować osobno za pomocą modułu lodash.clonedeep i jest to prawdopodobnie najlepszy wybór, jeśli jeszcze nie korzystasz z biblioteki zapewniającej funkcję głębokiego klonowaniaangular.copy
jQuery.extend(true, { }, oldObject)
;.clone()
klonuje tylko elementy DOMES6
Dla kompletności zwróć uwagę, że ES6 oferuje dwa mechanizmy płytkiej kopii:
Object.assign()
i składnię stron widzących . który kopiuje wartości wszystkich wyliczalnych własnych właściwości z jednego obiektu do drugiego. Na przykład:źródło
.clone()
, co nie jest właściwym kodem za pomocą w tym kontekście). Niestety to pytanie przeszło tak wiele wersji, że pierwotna dyskusja nie jest już nawet widoczna! Postępuj zgodnie z radą Corbana i napisz pętlę lub skopiuj właściwości bezpośrednio do nowego obiektu, jeśli zależy Ci na szybkości. Lub sprawdź to sam!Sprawdź ten test: http://jsben.ch/#/bWfk9
W moich poprzednich testach, gdzie szybkość była głównym problemem, stwierdziłem
być najwolniejszym sposobem głębokiego klonowania obiektu (jest wolniejszy niż jQuery.extend z
deep
flagą ustawioną na 10-20%).jQuery.extend działa dość szybko, gdy
deep
flaga jest ustawiona nafalse
(płytki klon). Jest to dobra opcja, ponieważ zawiera dodatkową logikę do sprawdzania poprawności typu i nie kopiuje niezdefiniowanych właściwości itp., Ale to również trochę spowolni.Jeśli znasz strukturę obiektów, które próbujesz sklonować lub możesz uniknąć głęboko zagnieżdżonych tablic, możesz napisać prostą
for (var i in obj)
pętlę, aby sklonować obiekt podczas sprawdzania hasOwnProperty i będzie on znacznie szybszy niż jQuery.Wreszcie, jeśli próbujesz sklonować znaną strukturę obiektu w gorącej pętli, możesz uzyskać DUŻO WIĘKSZĄ WYDAJNOŚĆ, po prostu wstawiając procedurę klonowania i ręcznie konstruując obiekt.
Mechanizmy śledzenia JavaScript są do bani w optymalizacji
for..in
pętli, a sprawdzenie hasOwnProperty również spowolni. Klonowanie ręczne, gdy prędkość jest absolutną koniecznością.Uważaj przy użyciu
JSON.parse(JSON.stringify(obj))
metody naDate
obiektach -JSON.stringify(new Date())
zwraca ciąg znaków reprezentujący datę w formacie ISO, któryJSON.parse()
nie jest konwertowany z powrotem naDate
obiekt. Zobacz tę odpowiedź, aby uzyskać więcej informacji .Dodatkowo pamiętaj, że przynajmniej w Chrome 65 natywne klonowanie nie jest dobrym rozwiązaniem. Według JSPerf, natywne klonowanie poprzez utworzenie nowej funkcji jest prawie 800 razy wolniejsze niż użycie JSON.stringify, który jest niesamowicie szybki na całej planszy.
Aktualizacja dla ES6
Jeśli używasz Javascript ES6, wypróbuj tę natywną metodę klonowania lub płytkiej kopii.
źródło
keys
z twojegoobject
, które mająfunctions
jako wartości, ponieważJSON
nie obsługuje funkcji.JSON.parse(JSON.stringify(obj))
obiektów w dniu spowoduje również konwersję daty z powrotem do UTC w postaci ciągu znaków w formacie ISO8601 .Zakładając, że masz tylko zmienne, a nie jakieś funkcje w swoim obiekcie, możesz po prostu użyć:
źródło
JSON
jest implementowany w natywnym kodzie (w większości przeglądarek), będzie to znacznie szybsze niż przy użyciu jakiegokolwiek innego rozwiązania do głębokiego kopiowania opartego na javascript, a czasem może być szybsze niż technika płytkiego kopiowania oparta na javascript (patrz: jsperf.com/cloning -an-object / 79 ).JSON.stringify({key: undefined}) //=> "{}"
Date
obiekty przechowywane w obiekcie, przekształcając je w ciąg znaków.Klonowanie strukturalne
Standard HTML zawiera wewnętrzny strukturalny algorytm klonowania / serializacji, który może tworzyć głębokie klony obiektów. Nadal jest ograniczony do niektórych wbudowanych typów, ale oprócz kilku typów obsługiwanych przez JSON obsługuje także Daty, RegExps, Mapy, Zestawy, Obiekty Blob, Listy plików, ImageDatas, Rzadkie Tablice, Tablice Typowane i prawdopodobnie więcej w przyszłości . Zachowuje również odwołania w sklonowanych danych, umożliwiając obsługę struktur cyklicznych i rekurencyjnych, które powodowałyby błędy dla JSON.
Wsparcie w Node.js: Experimental 🙂
v8
Moduł w node.js obecnie (od węzła 11) naraża strukturze serializacji API bezpośrednio , ale ta funkcjonalność jest nadal oznaczone jako „eksperymentalne”, i mogą ulec zmianie lub usunięciu w przyszłych wersjach. Jeśli używasz kompatybilnej wersji, klonowanie obiektu jest tak proste, jak:Bezpośrednie wsparcie w przeglądarkach: może ostatecznie? 😐
Przeglądarki nie zapewniają obecnie bezpośredniego interfejsu dla strukturalnego algorytmu klonowania, ale
structuredClone()
funkcja globalna została omówiona w whatwg / html # 793 na GitHub . Zgodnie z obecnie proponowanym użyciem do większości celów byłoby tak proste, jak:Jeśli nie zostanie to dostarczone, strukturyzowane implementacje klonów przeglądarki są ujawniane tylko pośrednio.
Obejście asynchroniczne: możliwe do użycia. 😕
Niższym sposobem na utworzenie strukturalnego klonu z istniejącymi interfejsami API jest opublikowanie danych przez jeden port MessageChannels . Drugi port wyemituje
message
zdarzenie ze strukturalnym klonem dołączonego.data
. Niestety nasłuchiwanie tych zdarzeń jest z konieczności asynchroniczne, a synchroniczne alternatywy są mniej praktyczne.Przykład użycia:
Obejścia synchroniczne: Okropne! 🤢
Nie ma dobrych opcji synchronicznego tworzenia uporządkowanych klonów. Oto kilka niepraktycznych hacków.
history.pushState()
ihistory.replaceState()
oba tworzą ustrukturyzowany klon pierwszego argumentu i przypisują tę wartość dohistory.state
. Możesz użyć tego do utworzenia strukturalnego klonu dowolnego obiektu takiego jak ten:Przykład użycia:
Pokaż fragment kodu
Chociaż synchroniczny, może być bardzo wolny. Powoduje to narzut związany z manipulowaniem historią przeglądarki. Wielokrotne wywoływanie tej metody może spowodować, że Chrome przestanie odpowiadać.
Notification
Konstruktor tworzy strukturze klon związanych z nią danych. Próbuje również wyświetlić użytkownikowi powiadomienie z przeglądarki, ale po cichu zakończy się niepowodzeniem, chyba że użytkownik poprosi o pozwolenie na powiadomienie. Jeśli masz pozwolenie na inne cele, natychmiast zamkniemy utworzone przez nas powiadomienie.Przykład użycia:
Pokaż fragment kodu
źródło
history.pushState()
ihistory.replaceState()
synchronicznie ustawionehistory.state
na ustrukturyzowanego klona pierwszego argumentu. Trochę dziwne, ale działa. Aktualizuję teraz swoją odpowiedź.Jeśli nie było żadnego wbudowanego, możesz spróbować:
źródło
Wydajny sposób klonowania (nie głębokiego klonowania) obiektu 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
Kod:
Test:
źródło
var obj = {}
iobj.a = obj
from.constructor
jestDate
na przykład. Jakif
można osiągnąć trzeci test, gdy drugiif
test zakończy się powodzeniem i spowoduje powrót funkcji (od tego czasuDate != Object && Date != Array
)?Tego używam:
źródło
cloneObject({ name: null })
=>{"name":{}}
typeof null > "object"
aleObject.keys(null) > TypeError: Requested keys of a value that is not an object.
zmień warunek naif(typeof(obj[i])=="object" && obj[i]!=null)
Głębokie kopiowanie według wydajności: od najlepszej do najgorszej
Głęboko skopiuj tablicę ciągów lub liczb (jeden poziom - bez wskaźników odniesienia):
Gdy tablica zawiera liczby i łańcuchy - funkcje takie jak .slice (), .concat (), .splice (), operator przypisania „=” i funkcja klonowania Underscore.js; utworzy głęboką kopię elementów tablicy.
Jeśli zmiana przypisania ma najszybszą wydajność:
A .slice () ma lepszą wydajność niż .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
Głęboko skopiuj tablicę obiektów (dwa lub więcej poziomów - wskaźniki odniesienia):
Napisz niestandardową funkcję (ma wyższą wydajność niż $ .extend () lub JSON.parse):
Użyj funkcji narzędziowych innych firm:
Tam, gdzie $ .extend jQuery ma lepszą wydajność:
źródło
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
źródło
Głębokie kopiowanie obiektów w JavaScript (myślę, że najlepsze i najprostsze)
1. Korzystanie z JSON.parse (JSON.stringify (obiekt));
2.Zastosowanie utworzonej metody
3. Korzystanie z linku _.cloneDeep Lo-Dash
4. Korzystanie z metody Object.assign ()
ALE ŹLE KIEDY
5.Korzystanie z Underscore.js _.clone link Underscore.js
ALE ŹLE KIEDY
JSBEN.CH Performance Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd
źródło
Object.assign()
nie wykonuje głębokiej kopiilet data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();
it throw:TypeError: tmp.title.pop is not a function
(oczywiście pop () działa dobrze, jeśli tylkodo let tmp = data
; ale potem nie mogę modyfikować tmp bez wpływu na dane)Istnieje biblioteka (zwana „klon”) , która robi to całkiem dobrze. Zapewnia najbardziej kompletne rekurencyjne klonowanie / kopiowanie dowolnych obiektów, jakie znam. Obsługuje również odwołania cykliczne, które nie są jeszcze objęte innymi odpowiedziami.
Możesz znaleźć na npm . Może być używany zarówno w przeglądarce, jak i Node.js.
Oto przykład, jak go używać:
Zainstaluj za pomocą
lub zapakuj go w Endera .
Możesz także pobrać kod źródłowy ręcznie.
Następnie możesz użyć go w kodzie źródłowym.
(Uwaga: Jestem autorem biblioteki).
źródło
JSON.parse(JSON.stringify(obj))
?Cloning
obiekt zawsze był przedmiotem zainteresowania w JS, ale o to chodziło przed ES6, poniżej wymieniam różne sposoby kopiowania obiektu w JavaScript, wyobraź sobie, że masz obiekt poniżej i chciałbyś mieć jego głęboką kopię:Istnieje kilka sposobów skopiowania tego obiektu bez zmiany źródła:
1) ES5 +, korzystając z prostej funkcji, aby wykonać kopię za Ciebie:
2) ES5 +, używając JSON.parse i JSON.stringify.
3) AngularJs:
4) jQuery:
5) UnderscoreJs & Loadash:
Mam nadzieję, że te pomoc ...
źródło
Object.assign
nie nie głęboko skopiować. Przykład:var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"
. Gdyby to była głęboka kopia,y.a.b
nadal by byłac
, ale jest terazd
.Wiem, że to stary post, ale pomyślałem, że może to pomóc następnej osobie, która się potyka.
Tak długo, jak nie przypisujesz obiektu do czegokolwiek, nie zachowuje on żadnych odniesień w pamięci. Aby stworzyć obiekt, który chcesz udostępnić innym obiektom, musisz utworzyć fabrykę w następujący sposób:
źródło
const defaultFoo = { a: { b: 123 } };
możesz iść,const defaultFoo = () => ({ a: { b: 123 } };
a twój problem został rozwiązany. Jednak tak naprawdę nie jest to odpowiedź na pytanie. Mogłoby to mieć większy sens jako komentarz do pytania, a nie pełna odpowiedź.Jeśli go używasz, biblioteka Underscore.js ma metodę klonowania .
źródło
.clone(...)
metody biblioteki narzędziowej . Każda duża biblioteka będzie je mieć, a powtarzające się krótkie, nieokreślone odpowiedzi nie będą przydatne dla większości odwiedzających, którzy nie będą korzystać z tej konkretnej biblioteki.Oto wersja powyższej odpowiedzi ConroyP, która działa, nawet jeśli konstruktor ma wymagane parametry:
Ta funkcja jest również dostępna w moim bibliotece simpleoo .
Edytować:
Oto bardziej niezawodna wersja (dzięki Justinowi McCandlessowi obsługuje teraz także cykliczne odwołania):
źródło
Poniższe tworzy dwa wystąpienia tego samego obiektu. Znalazłem i używam go obecnie. Jest prosty i łatwy w użyciu.
źródło
Crockford sugeruje (i wolę) użycie tej funkcji:
Jest zwięzły, działa zgodnie z oczekiwaniami i nie potrzebujesz biblioteki.
EDYTOWAĆ:
Jest to polifill dla
Object.create
, więc możesz również z niego korzystać.UWAGA: W przypadku korzystania z niektórych z tych funkcji mogą wystąpić problemy z korzystaniem z iteracji
hasOwnProperty
. Ponieważcreate
utwórz nowy pusty obiekt, który będzie dziedziczyłoldObject
. Ale nadal jest przydatny i praktyczny do klonowania obiektów.Na przykład jeśli
oldObject.a = 5;
ale:
źródło
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-patternsLodash ma niezłą metodę _.cloneDeep (wartość) :
źródło
.clone(...)
metody biblioteki narzędziowej . Każda duża biblioteka będzie je mieć, a powtarzające się krótkie, nieokreślone odpowiedzi nie będą przydatne dla większości odwiedzających, którzy nie będą korzystać z tej konkretnej biblioteki._.merge({}, objA)
. Gdyby tylko lodash nie mutował obiektów,clone
funkcja nie byłaby konieczna.źródło
Płytka kopia w jednym wierszu ( ECMAScript 5. edycja ):
I płytka kopia jednowarstwowa ( ECMAScript 6 edycja , 2015):
źródło
Object.keys
go pomija właściwości niepoliczalne i dziedziczone. Ponadto traci deskryptory właściwości, wykonując bezpośrednie przypisanie.Tylko dlatego, że nie widziałem o AngularJS i pomyślałem, że ludzie mogą chcieć wiedzieć ...
angular.copy
zapewnia również metodę głębokiego kopiowania obiektów i tablic.źródło
angular.extend({},obj);
jQuery.extend
iangular.extend
są obie kopie płytkie.angular.copy
jest głęboką kopią.Wydaje się, że nie ma jeszcze idealnego operatora głębokiego klonowania dla obiektów podobnych do tablicy. Jak ilustruje poniższy kod, kloner jQuery Johna Resiga przekształca tablice o właściwościach nienumerycznych w obiekty, które nie są tablicami, a kloner JSON firmy RegDwight upuszcza właściwości nienumeryczne. Poniższe testy ilustrują te punkty w wielu przeglądarkach:
źródło
Mam dwie dobre odpowiedzi w zależności od tego, czy Twoim celem jest sklonowanie „zwykłego, starego obiektu JavaScript”, czy nie.
Załóżmy również, że Twoim celem jest utworzenie pełnego klonu bez odwołań do prototypu z powrotem do obiektu źródłowego. Jeśli nie jesteś zainteresowany pełnym klonowaniem, możesz użyć wielu procedur Object.clone () podanych w niektórych innych odpowiedziach (wzór Crockforda).
W przypadku zwykłych starych obiektów JavaScript wypróbowany i naprawdę dobry sposób klonowania obiektu we współczesnych środowiskach wykonawczych jest po prostu:
Zauważ, że obiekt źródłowy musi być czystym obiektem JSON. Oznacza to, że wszystkie jego zagnieżdżone właściwości muszą być skalarami (takimi jak logiczna, łańcuch, tablica, obiekt itp.). Żadne funkcje ani obiekty specjalne, takie jak RegExp lub Date, nie będą klonowane.
Czy to jest wydajne? Pewnie, ze tak. Wypróbowaliśmy wszystkie metody klonowania i to działa najlepiej. Jestem pewien, że jakiś ninja wyczaruje szybszą metodę. Ale podejrzewam, że mówimy o marginalnych zyskach.
To podejście jest po prostu proste i łatwe do wdrożenia. Zapakuj go w wygodną funkcję, a jeśli naprawdę chcesz wycisnąć trochę korzyści, idź później.
Teraz w przypadku nieprzezroczystych obiektów JavaScript nie ma naprawdę prostej odpowiedzi. W rzeczywistości nie może być tak z powodu dynamicznej natury funkcji JavaScript i wewnętrznego stanu obiektu. Głębokie klonowanie struktury JSON z funkcjami wewnątrz wymaga odtworzenia tych funkcji i ich wewnętrznego kontekstu. A JavaScript po prostu nie ma znormalizowanego sposobu na zrobienie tego.
Prawidłowy sposób, aby to zrobić, jeszcze raz, polega na wygodnej metodzie, którą deklarujesz i używasz ponownie w swoim kodzie. Metodę wygody można wyposażyć w pewne zrozumienie własnych obiektów, aby zapewnić prawidłowe odtworzenie wykresu w nowym obiekcie.
Napisaliśmy własne, ale najlepsze ogólne podejście, jakie widziałem, jest tutaj:
http://davidwalsh.name/javascript-clone
To jest właściwy pomysł. Autor (David Walsh) skomentował klonowanie funkcji uogólnionych. Jest to coś, co możesz zrobić, w zależności od przypadku użycia.
Główną ideą jest to, że musisz specjalnie obsługiwać tworzenie instancji swoich funkcji (lub klas prototypowych, że tak powiem) dla poszczególnych typów. Tutaj podał kilka przykładów RegExp i Date.
Ten kod jest nie tylko krótki, ale także bardzo czytelny. Jest dość łatwy do rozszerzenia.
Czy to jest wydajne? Pewnie, ze tak. Biorąc pod uwagę, że celem jest stworzenie prawdziwego klonowania w głębokiej kopii, będziesz musiał przejść elementy wykresu obiektu źródłowego. Dzięki takiemu podejściu możesz dokładnie dostosować, które elementy podrzędne mają być traktowane i jak ręcznie obsługiwać typy niestandardowe.
Więc proszę bardzo. Dwa podejścia. Oba są moim zdaniem wydajne.
źródło
Nie jest to na ogół najbardziej wydajne rozwiązanie, ale robi to, czego potrzebuję. Proste przypadki testowe poniżej ...
Cykliczny test tablicowy ...
Test działania ...
źródło
AngularJS
Cóż, jeśli używasz kąta, możesz to zrobić
źródło
Nie zgadzam się z odpowiedzią z największą liczbą głosów tutaj . Recursive Głębokie Clone jest znacznie szybsze niż JSON.parse (JSON.stringify (obj)) podejścia wymienione.
Oto funkcja szybkiego odniesienia:
źródło
if(o instanceof Date) return new Date(o.valueOf());
po sprawdzeniu, czy null `źródło
Tylko wtedy, gdy możesz użyć ECMAScript 6 lub transpilatorów .
Funkcje:
Kod:
źródło
Oto kompleksowa metoda clone (), która może klonować dowolny obiekt JavaScript. Obsługuje prawie wszystkie przypadki:
źródło