Skip to content
Skip to content
Menu
Info Cafe
Info Cafe

Kopiowanie obiektów w JavaScript

By admin on 10 stycznia, 2021

Obiekty są podstawowymi blokami języka JavaScript. Obiekt jest kolekcją właściwości, a właściwość jest związkiem pomiędzy kluczem (lub nazwą) i wartością. Prawie wszystkie obiekty w JavaScript są instancjami Object, który znajduje się na szczycie łańcucha prototypów.

Wprowadzenie

Jak wiadomo, operator przypisania nie tworzy kopii obiektu, a jedynie przypisuje do niego referencję, przyjrzyjmy się następującemu kodowi:

let obj = { a: 1, b: 2,};let copy = obj;obj.a = 5;console.log(copy.a);// Result // a = 5;

Zmienna obj jest pojemnikiem na zainicjalizowany nowy obiekt. Zmienna copy wskazuje na ten sam obiekt i jest odniesieniem do tego obiektu. Więc w zasadzie ten { a: 1, b: 2, } obiekt mówi: Istnieją teraz dwa sposoby, aby uzyskać do mnie dostęp. Musisz przejść przez zmienną obj lub zmienną copy w każdym razie nadal się do mnie dostaniesz i wszystko co zrobisz ze mną poprzez te sposoby (bramy) będzie miało na mnie wpływ.

Immutowalność jest szeroko omawiana w tych dniach i musisz słuchać tego połączenia! Ta metoda usuwa jakąkolwiek formę niezmienności i może prowadzić do błędów, jeśli oryginalny obiekt zostanie użyty przez inną część twojego kodu.

Naiwny sposób kopiowania obiektów

Naiwny sposób kopiowania obiektów polega na zapętlaniu oryginalnego obiektu i kopiowaniu każdej właściwości po kolei. Przyjrzyjmy się temu kodowi:

function copy(mainObj) { let objCopy = {}; // objCopy will store a copy of the mainObj let key; for (key in mainObj) { objCopy = mainObj; // copies each property to the objCopy object } return objCopy;}const mainObj = { a: 2, b: 5, c: { x: 7, y: 4, },}console.log(copy(mainObj));

Inherent Issues

  1. objCopy Obiekt ma nową Object.prototype metodę różną od metody mainObj prototypu obiektu, co nie jest tym, czego chcemy. Chcemy dokładnej kopii oryginalnego obiektu.
  2. Deskryptory właściwości nie są kopiowane. Deskryptor „writable” z wartością ustawioną na false będzie miał wartość true w obiekcie objCopy.
  3. Powyższy kod kopiuje tylko enumeratywne właściwości mainObj.
  4. Jeśli jedna z właściwości w oryginalnym obiekcie jest obiektem samym w sobie, to będzie ona współdzielona pomiędzy kopią a oryginałem sprawiając, że ich odpowiednie właściwości będą wskazywać na ten sam obiekt.

Płytkie kopiowanie obiektów

Obiekt jest płytko skopiowany, gdy źródłowe właściwości najwyższego poziomu są skopiowane bez żadnego odniesienia i istnieje właściwość źródłowa, której wartość jest obiektem i jest skopiowana jako odniesienie. Jeśli wartość źródłowa jest referencją do obiektu, to kopiuje tylko tę wartość referencyjną do obiektu docelowego.

Płytka kopia zduplikuje właściwości najwyższego poziomu, ale zagnieżdżony obiekt jest współdzielony pomiędzy oryginałem (źródło) i kopią (cel).

Używanie metody Object.assign()

Metoda Object.assign() służy do kopiowania wartości wszystkich enumeratywnych własnych właściwości z jednego lub więcej obiektów źródłowych do obiektu docelowego.

let obj = { a: 1, b: 2,};let objCopy = Object.assign({}, obj);console.log(objCopy);// Result - { a: 1, b: 2 }

Dobrze, to jak na razie wykonuje zadanie. Stworzyliśmy kopię obj. Zobaczmy, czy niezmienność istnieje:

let obj = { a: 1, b: 2,};let objCopy = Object.assign({}, obj);console.log(objCopy); // result - { a: 1, b: 2 }objCopy.b = 89;console.log(objCopy); // result - { a: 1, b: 89 }console.log(obj); // result - { a: 1, b: 2 }

W powyższym kodzie, zmieniliśmy wartość właściwości 'b' w obiekcie objCopy na 89 i kiedy logujemy zmodyfikowany obiekt objCopy w konsoli, zmiany dotyczą tylko objCopy. Ostatnia linia kodu sprawdza, czy obiekt obj jest nadal nienaruszony i nie uległ zmianie. Oznacza to, że udało nam się utworzyć kopię obiektu źródłowego bez żadnych odniesień do niego.

Pitfall of Object.assign()

Nie tak szybko! Chociaż udało nam się stworzyć kopię i wszystko wydaje się działać dobrze, pamiętasz, że rozmawialiśmy o płytkim kopiowaniu? Spójrzmy na ten przykład:

let obj = { a: 1, b: { c: 2, },}let newObj = Object.assign({}, obj);console.log(newObj); // { a: 1, b: { c: 2} }obj.a = 10;console.log(obj); // { a: 10, b: { c: 2} }console.log(newObj); // { a: 1, b: { c: 2} }newObj.a = 20;console.log(obj); // { a: 10, b: { c: 2} }console.log(newObj); // { a: 20, b: { c: 2} }newObj.b.c = 30;console.log(obj); // { a: 10, b: { c: 30} }console.log(newObj); // { a: 20, b: { c: 30} }// Note: newObj.b.c = 30; Read why..

Dlaczego obj.b.c = 30?

Cóż, to jest pułapka Object.assign()Object.assign tworzy tylko płytkie kopie. Zarówno newObj.b, jak i obj.b współdzielą to samo odniesienie do obiektu, ponieważ indywidualne kopie nie zostały wykonane, zamiast tego skopiowano odniesienie do obiektu. Każda zmiana dokonana w dowolnej właściwości obiektu ma zastosowanie do wszystkich odniesień korzystających z obiektu. Jak możemy to naprawić? Czytaj dalej… mamy poprawkę w następnej sekcji.

Uwaga: Właściwości na łańcuchu prototypów i właściwości nieenumerowalne nie mogą być kopiowane. Zobacz tutaj:

let someObj = { a: 2,}let obj = Object.create(someObj, { b: { value: 2, }, c: { value: 3, enumerable: true, },});let objCopy = Object.assign({}, obj);console.log(objCopy); // { c: 3 }

  • someObj jest na łańcuchu prototypu obj’a więc nie zostanie skopiowany.
  • property b jest właściwością niewyliczalną.
  • property c ma deskryptor właściwości enumeratywnej pozwalający na wyliczanie. Dlatego właśnie został skopiowany.

Głębokie kopiowanie obiektów

Głębokie kopiowanie zduplikuje każdy obiekt, który napotka. Kopia i oryginalny obiekt nie będą miały nic wspólnego, więc będzie to kopia oryginału. Oto rozwiązanie problemu, który napotkaliśmy używając Object.assign(). Zbadajmy to.

Użycie JSON.parse(JSON.stringify(object));

To naprawia problem, który mieliśmy wcześniej. Teraz newObj.b ma kopię, a nie referencję! Jest to sposób na głębokie kopiowanie obiektów. Oto przykład:

let obj = { a: 1, b: { c: 2, },}let 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 } } (New Object Intact!)

Immutable: ✓

Pitfall

Niestety, ta metoda nie może być użyta do kopiowania metod obiektów zdefiniowanych przez użytkownika. Zobacz poniżej.

Kopiowanie metod obiektu

Metoda jest właściwością obiektu, która jest funkcją. W dotychczasowych przykładach nie kopiowaliśmy obiektu z metodą. Spróbujmy to teraz zrobić i wykorzystajmy poznane metody do tworzenia kopii.

let obj = { name: 'scotch.io', exec: function exec() { return true; },}let method1 = Object.assign({}, obj);let method2 = JSON.parse(JSON.stringify(obj));console.log(method1); //Object.assign({}, obj)/* result{ exec: function exec() { return true; }, name: "scotch.io"}*/console.log(method2); // JSON.parse(JSON.stringify(obj))/* result{ name: "scotch.io"}*/

Wynik pokazuje, że Object.assign() może być użyty do kopiowania metod, natomiast JSON.parse(JSON.stringify(obj)) nie może być użyty.

Kopiowanie obiektów kołowych

Obiekty kołowe to obiekty, które mają właściwości odwołujące się do samych siebie. Użyjmy metod kopiowania obiektów, które poznaliśmy do tej pory, aby utworzyć kopie obiektu kołowego i zobaczmy, czy to działa.

Użycie JSON.parse(JSON.stringify(object))

Spróbujmy JSON.parse(JSON.stringify(object)):

// circular objectlet obj = { a: 'a', b: { c: 'c', d: 'd', },}obj.c = obj.b;obj.e = obj.a;obj.b.c = obj.c;obj.b.d = obj.b;obj.b.e = obj.b.c;let newObj = JSON.parse(JSON.stringify(obj));console.log(newObj); 

Oto wynik:

JSON.parse(JSON.stringify(obj)) wyraźnie nie działa dla okrągłych obiektów.

Użycie Object.assign()

Spróbujmy Object.assign():

// circular objectlet obj = { a: 'a', b: { c: 'c', d: 'd', },}obj.c = obj.b;obj.e = obj.a;obj.b.c = obj.c;obj.b.d = obj.b;obj.b.e = obj.b.c;let newObj2 = Object.assign({}, obj);console.log(newObj2); 

Oto wynik:

Object.assign() działa dobrze dla płytkiego kopiowania okrągłych obiektów, ale nie działałby dla głębokiego kopiowania. Zapraszam do zbadania circular object tree na konsoli przeglądarki. Jestem pewien, że znajdziesz tam wiele interesującej pracy.

Używanie elementów rozproszonych ( … )

ES6 ma już zaimplementowane elementy spoczynkowe do przypisywania destruktorów tablicowych i elementy rozproszone do literałów tablicowych. Spójrz na implementację elementu spread na tablicy tutaj:

const array = ;const newArray = ;console.log(newArray);// Result // 

Właściwość spread dla literałów obiektów jest obecnie propozycją Stage 3 dla ECMAScript. Właściwości rozprzestrzeniania w inicjalizatorach obiektów kopiują własne enumerowalne właściwości z obiektu źródłowego na obiekt docelowy. Poniższy przykład pokazuje, jak łatwo byłoby skopiować obiekt, gdy propozycja zostanie zaakceptowana.

let obj = { one: 1, two: 2,}let newObj = { ...z };// { one: 1, two: 2 }

Uwaga: To będzie skuteczne tylko dla płytkiego kopiowania

Wniosek

Kopiowanie obiektów w JavaScript może być dość zniechęcające, szczególnie jeśli jesteś nowy w JavaScript i nie znasz języka. Mam nadzieję, że ten artykuł pomógł Ci zrozumieć i uniknąć przyszłych pułapek, które możesz napotkać kopiując obiekty. Jeśli masz jakąś bibliotekę lub kawałek kodu, który pozwala osiągnąć lepszy rezultat, podziel się nim ze społecznością. Szczęśliwego kodowania!

Zobacz wpisy

Unfunded Mandates, Examples, and the Need for UMRA (Polski)
How To Make Sugar Glass – AWESOME Edible Glass Candy Recipe For Kids! (Polski)

Dodaj komentarz Anuluj pisanie odpowiedzi

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Najnowsze wpisy

  • Firebush (Polski)
  • Prognoza stawek CD na 2021 rok: Stopy procentowe prawdopodobnie pozostaną na niskim poziomie, ale mogą wzrosnąć w dalszej części roku
  • Jak ustrukturyzować dokumentację systemu zarządzania jakością
  • Zdrowe Gry i Zajęcia dla Dzieci | UIC Online Informatics
  • Wheat Ales (American) (Polski)
  • Korzyści z karmienia piersią po roku
  • Czy bezpiecznie jest wrzucać fusy z kawy do zlewu | Atomic Plumbing
  • Cool-Down After Your Workout (Polski)
  • Nasza praca
  • Najlepsza ręczna maszyna do szycia do kupienia: 2020

Meta

  • Zaloguj się
  • Kanał wpisów
  • Kanał komentarzy
  • WordPress.org

Archiwa

  • Marzec 2021
  • Luty 2021
  • Styczeń 2021
  • Grudzień 2020
  • DeutschDeutsch
  • NederlandsNederlands
  • EspañolEspañol
  • FrançaisFrançais
  • PortuguêsPortuguês
  • ItalianoItaliano
  • PolskiPolski
  • 日本語日本語
©2021 Info Cafe | WordPress Theme by SuperbThemes.com