Hogyan lehet megkülönböztetni a mély és a sekély példányokat a JavaScript-ben

Az új mindig jobb!

Korábban már egészen biztosan foglalkozott a JavaScript-példányokkal, még akkor is, ha nem tudta. Talán Ön is hallott már arról a paradigmáról a funkcionális programozásban, hogy nem szabad módosítania a meglévő adatokat. Ehhez tudnia kell, hogyan lehet biztonságosan lemásolni az értékeket a JavaScript-be. Ma megvizsgáljuk, hogyan lehet ezt megtenni, miközben elkerüljük a buktatókat!

Először is, mi az a másolat?

Egy példány csak úgy néz ki, mint a régi, de nem az. Ha megváltoztatja a másolatot, akkor azt várja, hogy az eredeti változatlan marad, míg a másolat megváltozik.

A programozás során az értékeket változókban tároljuk. Másolat készítése azt jelenti, hogy új változót indít ugyanazzal az értékkel. Mindazonáltal nagy potenciális buktatót kell figyelembe venni: mély másolás vagy sekély másolás . A mély másolat azt jelenti, hogy az új változó összes értéke átmásolódik és leválik az eredeti változóról. A sekély másolat azt jelenti, hogy bizonyos (al) értékek továbbra is kapcsolódnak az eredeti változóhoz.

Ahhoz, hogy valóban megértse a másolást, be kell jutnia, hogyan tárolja a JavaScript az értékeket.

Primitív adattípusok

A primitív adattípusok a következők:

  • Szám - pl 1
  • Húr - pl 'Hello'
  • Logikai - pl true
  • undefined
  • null

Amikor létrehozza ezeket az értékeket, szorosan kapcsolódnak a hozzájuk rendelt változóhoz. Csak egyszer léteznek. Ez azt jelenti, hogy nem igazán kell aggódnia a primitív adattípusok JavaScript-be történő másolása miatt. Ha másolatot készít, az igazi másolat lesz. Lássunk egy példát:

const a = 5
let b = a // this is the copy
b = 6
console.log(b) // 6
console.log(a) // 5

A végrehajtással b = aelkészíti a másolatot. Most, amikor új értéket rendel hozzá b, a bváltozások értéke , de nem a értéke a.

Összetett adattípusok - Objektumok és tömbök

Technikailag a tömbök is objektumok, tehát ugyanúgy viselkednek. Később mindkettőjüket részletesen átnézem.

Itt érdekesebbé válik. Ezeket az értékeket valójában csak egyszer tárolják, amikor példányosítják, és egy változó hozzárendelése csak egy mutatót (hivatkozást) hoz létre az értékhez .

Most, ha teszünk egy példányát b = a, és változtatni néhány beágyazott értéket b, akkor tulajdonképpen megváltoztatja a„s beágyazott érték is, hiszen a, és btulajdonképpen pont ugyanazt a dolgot. Példa:

const a = {
 en: 'Hello',
 de: 'Hallo',
 es: 'Hola',
 pt: 'Olà'
}
let b = a
b.pt = 'Oi'
console.log(b.pt) // Oi
console.log(a.pt) // Oi

A fenti példában valóban készítettünk egy sekély másolatot . Ez sokszor problematikus, mivel azt várjuk, hogy a régi változónak az eredeti értékei vannak, nem pedig a megváltozottak. Amikor hozzáférünk hozzá, néha hibát kapunk. Előfordulhat, hogy megpróbálja egy ideig hibakeresni, mielőtt megtalálja a hibát, mivel sok fejlesztő nem igazán fogja fel a koncepciót, és nem is számít arra, hogy ez a hiba.

Nézzük meg, hogyan készíthetünk másolatokat tárgyakról és tömbökről.

Tárgyak

Az objektumok másolásának többféle módja van, különösen az új kibővülő és javuló JavaScript specifikációval.

Spread operátor

Az ES2015-tel bevezetett operátor nagyon jó, mert olyan rövid és egyszerű. Ez az összes értéket egy új objektumba terjeszti. Használhatja a következőképpen:

const a = {
 en: 'Bye',
 de: 'Tschüss'
}
let b = {...a}
b.de = 'Ciao'
console.log(b.de) // Ciao
console.log(a.de) // Tschüss

Használhatja például két objektum egyesítésére is const c = {...a, ...b}.

Objektum. Hozzárendelés

Ezt leginkább akkor használták, mielőtt a spread operátor körül volt, és alapvetően ugyanazt csinálja. Vigyázni kell azonban, mivel a Object.assign()módszer első argumentuma valóban módosul és visszatér. Tehát ne felejtse el átadni az objektumot a másolásnak legalább második argumentumként. Normál esetben csak egy üres objektumot adna meg első argumentumként, hogy megakadályozza a meglévő adatok módosítását.

const a = {
 en: 'Bye',
 de: 'Tschüss'
}
let b = Object.assign({}, a)
b.de = 'Ciao'
console.log(b.de) // Ciao
console.log(a.de) // Tschüss

Buktató: Beágyazott objektumok

Mint korábban említettük, az objektumok másolásával kapcsolatban egy nagy figyelmeztetés van, amely mindkét fent felsorolt ​​módszerre vonatkozik. Ha beágyazott objektum (vagy tömb) van, és másolja, akkor az adott objektumon belüli beágyazott objektumok nem lesznek másolva, mivel ezek csak mutatók / hivatkozások. Ezért, ha megváltoztatja a beágyazott objektumot, akkor azt mindkét esetben megváltoztatja, ami azt jelenti, hogy végül egy sekély másolatot készít . Példa: // ROSSZ PÉLDA

const a = {
 foods: {
 dinner: 'Pasta'
 }
}
let b = {...a}
b.foods.dinner = 'Soup' // changes for both objects
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Soup

A beágyazott objektumok mély másolatának elkészítéséhez ezt figyelembe kell vennie. Ennek megakadályozásának egyik módja az összes beágyazott objektum manuális másolása:

const a = {
 foods: {
 dinner: 'Pasta'
 }
}
let b = {foods: {...a.foods}}
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta

Abban az esetben, ha kíváncsi lenne, mit tegyen, ha az objektumnak több kulcsa van, csak foodsa spread operátor teljes potenciálját használhatja fel. Ha a tulajdonság után több tulajdonságot ad át ...spread, akkor például felülírják az eredeti értékeket const b = {...a, foods: {...a.foods}}.

Mély másolatok készítése gondolkodás nélkül

Mi van, ha nem tudja, hogy a beágyazott struktúrák milyen mélyek? Nagyon fárasztó lehet manuálisan átnézni a nagy objektumokat, és minden beágyazott objektumot kézzel másolni. Van úgy, hogy mindent gondolkodás nélkül lemásolunk. Egyszerűen stringifya tárgyad, és parserögtön utána:

const a = {
 foods: {
 dinner: 'Pasta'
 }
}
let b = JSON.parse(JSON.stringify(a))
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta

Itt figyelembe kell vennie, hogy nem fog tudni másolni egyedi osztálypéldányokat, ezért csak akkor használhatja, ha olyan objektumokat másol, amelyekben natív JavaScript-értékek vannak.

Tömbök

A tömbök másolása ugyanolyan gyakori, mint az objektumok másolása. A mögöttes logika nagy része hasonló, mivel a tömbök is csak tárgyak a motorháztető alatt.

Spread operátor

Az objektumokhoz hasonlóan a spread operátorral is másolhat egy tömböt:

const a = [1,2,3]
let b = [...a]
b[1] = 4
console.log(b[1]) // 4
console.log(a[1]) // 2

Tömbfüggvények - leképezés, szűrés, kicsinyítés

Ezek a módszerek új tömböt adnak vissza, az eredeti összes (vagy néhány) értékével. Ennek során módosíthatja az értékeket is, ami nagyon hasznos:

const a = [1,2,3]
let b = a.map(el => el)
b[1] = 4
console.log(b[1]) // 4
console.log(a[1]) // 2

Alternatív megoldásként megváltoztathatja a kívánt elemet másolás közben:

const a = [1,2,3]
const b = a.map((el, index) => index === 1 ? 4 : el)
console.log(b[1]) // 4
console.log(a[1]) // 2

Tömb.szelet

Ezt a módszert általában az elemek egy részhalmazának visszaküldésére használják, kezdve egy meghatározott indextől és adott esetben az eredeti tömb egy meghatározott indexével végződve. Amikor array.slice()vagy array.slice(0)akkor a végén egy másolatot az eredeti tömb.

const a = [1,2,3]
let b = a.slice(0)
b[1] = 4
console.log(b[1]) // 4
console.log(a[1]) // 2

Beágyazott tömbök

Az objektumokhoz hasonlóan, a fenti módszerek segítségével egy tömböt más belső tömböt vagy objektumot másolva, sekély másolatot generál . Ennek megakadályozása érdekében használja is JSON.parse(JSON.stringify(someArray)).

BÓNUSZ: az egyedi osztályok példányának másolása

Ha már profi vagy a JavaScript-ben, és foglalkozol egyedi konstruktorfüggvényeiddel vagy osztályaiddal, akkor valószínűleg ezek példányait is le akarod másolni.

Amint azt korábban említettük, ezeket nem lehet csak szigorítani + elemezni, mivel elveszítik az osztály metódusait. Ehelyett egyéni copymetódust szeretne hozzáadni egy új példány létrehozásához az összes régi értékkel. Lássuk, hogyan működik:

class Counter {
 constructor() {
 this.count = 5
 }
 copy() {
 const copy = new Counter()
 copy.count = this.count
 return copy
 }
}
const originalCounter = new Counter()
const copiedCounter = originalCounter.copy()
console.log(originalCounter.count) // 5
console.log(copiedCounter.count) // 5
copiedCounter.count = 7
console.log(originalCounter.count) // 5
console.log(copiedCounter.count) // 7

A példányon belül hivatkozott objektumok és tömbök kezeléséhez alkalmaznia kell újonnan elsajátított készségeit a mély másolással kapcsolatban ! Csak hozzáadok egy végleges megoldást az egyedi konstruktor copymódszerhez, hogy dinamikusabbá váljon:

Ezzel a másolási módszerrel annyi értéket helyezhet el a kivitelezőjében, hogy mindent manuálisan nem kell lemásolnia!

A szerzőről: Lukas Gisder-Dubé alapította és vezette startupot CTO-ként 1 1/2 évig, felépítve a technikai csapatot és az architektúrát. Miután elhagyta az indítást, az Ironhack vezető oktatójaként tanított kódolást, és most Berlini Startup Ügynökséget és Tanácsadást épít. A dube.io oldalon további információkat találhat.