Kuidas eristada JavaScripti sügavaid ja madalaid koopiaid

Uus on alati parem!

Kindlasti olete varem JavaScripti koopiatega tegelenud, isegi kui te seda ei teadnud. Võib-olla olete kuulnud ka funktsionaalse programmeerimise paradigmast, et te ei tohiks olemasolevaid andmeid muuta. Selleks peate teadma, kuidas JavaScripti väärtusi ohutult kopeerida. Täna vaatame, kuidas seda teha, vältides lõkse!

Esiteks, mis on koopia?

Eksemplar näeb lihtsalt välja nagu vana asi, kuid pole nii. Kui muudate koopiat, eeldate, et originaal jääb samaks, samas kui koopia muutub.

Programmeerimisel salvestame väärtused muutujatesse. Koopia tegemine tähendab, et algatate uue (te) sama (te) väärtusega muutuja. Siiski on vaja kaaluda suurt potentsiaalset lõksu: sügav kopeerimine vs madal kopeerimine . Sügavkoopia tähendab, et kõik uue muutuja väärtused kopeeritakse ja ühendatakse algsest muutujast lahti . Madal koopia tähendab, et teatud (ala) väärtused on endiselt seotud algse muutujaga.

Kopeerimisest tõeliselt aru saamiseks peate uurima, kuidas JavaScript väärtusi salvestab.

Ürgsed andmetüübid

Ürgandmetüübid hõlmavad järgmist:

  • Arv - nt 1
  • String - nt 'Hello'
  • Boolean - nt true
  • undefined
  • null

Nende väärtuste loomisel on need tihedalt seotud muutujaga, millele need on määratud. Nad eksisteerivad ainult üks kord. See tähendab, et te ei pea algsete andmetüüpide JavaScripti kopeerimise pärast muretsema. Kui teete koopia, on see tõeline koopia. Vaatame näite:

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

Käivitades b = ateed koopia. Kui määrate uue väärtuse ümber b, bmuutub muudatuste väärtus , kuid mitte väärtus a.

Liitandmetüübid - objektid ja massiivid

Tehniliselt on massiivid ka objektid, seega käituvad nad samamoodi. Mõlemad vaatan hiljem üksikasjalikult läbi.

Siin läheb huvitavamaks. Need väärtused salvestatakse hetkeseisuga tegelikult vaid üks kord ja muutuja määramine loob sellele väärtusele lihtsalt kursori (viite) .

Nüüd, kui me teeme koopia b = aning muuta mõned pesastatud väärtus b, siis tegelikult muudab a's pesastatud väärtus samuti, kuna aja bviitavad tegelikult sama asi. Näide:

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

Ülaltoodud näites tegime tegelikult madala koopia . See on sageli probleemne, kuna eeldame, et vanal muutujal on algsed väärtused, mitte muudetud. Sellele juurde pääsedes saame mõnikord vea. Võib juhtuda, et proovite seda enne vea leidmist mõnda aega siluda, kuna paljud arendajad ei mõista mõistet tegelikult ega arvata, et see on viga.

Vaatame, kuidas saame objektidest ja massiividest koopiaid teha.

Objektid

Objektidest saab koopiaid teha mitmel viisil, eriti uue laieneva ja täiustava JavaScripti spetsifikatsiooni abil.

Levikuoperaator

ES2015-ga tutvustatud operaator on lihtsalt suurepärane, kuna see on nii lühike ja lihtne. See "hajutab" kõik väärtused uueks objektiks. Saate seda kasutada järgmiselt:

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

Samuti saate seda kasutada näiteks kahe objekti ühendamiseks const c = {...a, ...b}.

Objekt.omistamine

Seda kasutati enamasti enne levikuoperaatori olemasolu ja see teeb põhimõtteliselt sama asja. Siiski peate olema ettevaatlik, kuna Object.assign()meetodi esimene argument muudetakse ja tagastatakse. Nii et veenduge, et edastaksite kopeeritava objekti vähemalt teise argumendina. Tavaliselt edastaksite esimese argumendina tühja objekti, et vältida olemasolevate andmete muutmist.

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

Lõkk: pesastatud objektid

Nagu varem mainitud, on objektide kopeerimisel üks suur hoiatus, mis kehtib mõlema eespool loetletud meetodi kohta. Kui teil on pesastatud objekt (või massiiv) ja see kopeeritakse, ei kopeerita selle objekti sees olevaid pesastatud objekte, kuna need on ainult viidad / viited. Seega, kui muudate pesastatud objekti, muudate seda ka mõlemal juhul, see tähendab, et teete uuesti madala koopia . Näide: // HALB NÄIDE

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

Et sügav koopia pesastatud objektid , siis oleks arvata, et. Üks viis selle vältimiseks on kõigi pesastatud objektide käsitsi kopeerimine:

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

Kui mõtlete, mida teha, kui objektil on rohkem võtmeid kui ainult foods, võite kasutada hajutamisoperaatori kogu potentsiaali. Pärast atribuutide edastamist ...spreadkirjutavad need näiteks üle algsed väärtused const b = {...a, foods: {...a.foods}}.

Sügavate koopiate tegemine mõtlemata

Mis siis, kui te ei tea, kui sügavad on pesastatud struktuurid? Suurte objektide käsitsi läbimine ja iga pesastatud objekti käsitsi kopeerimine võib olla väga tüütu. On võimalus kopeerida kõik mõtlemata. Sa lihtsalt stringifyoma objekti ja parsesee kohe pärast:

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

Siin peate arvestama, et te ei saa kohandatud klassieksemplare kopeerida, nii et saate seda kasutada ainult siis, kui kopeerite objekte, mille sees on natiivsed JavaScripti väärtused .

Massiivid

Massiivide kopeerimine on sama levinud kui objektide kopeerimine. Suur osa selle taga olevast loogikast on sarnane, kuna massiivid on ka lihtsalt kapoti all olevad objektid.

Levikuoperaator

Nagu objektide puhul, saate massiivi kopeerimiseks kasutada hajutusoperaatorit:

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

Massiivi funktsioonid - kaardistamine, filtreerimine, vähendamine

Need meetodid tagastavad uue massiivi koos kõigi (või mõne) algse väärtusega. Seda tehes saate ka väärtusi muuta, mis on väga kasulik:

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

Teise võimalusena saate kopeerimisel soovitud elementi muuta:

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

Massiiv. Viil

Seda meetodit kasutatakse tavaliselt elementide alamhulga tagastamiseks, alustades kindlast indeksist ja valikuliselt lõpetades algse massiivi kindla indeksiga. Kasutades array.slice()või array.slice(0)saate lõpuks originaalse massiivi koopia.

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

Pesastatud massiivid

Sarnaselt objektidele loob ülaltoodud meetodite abil massiivi kopeerimine teise massiivi või objekti sees madala koopia . Selle vältimiseks kasutage ka JSON.parse(JSON.stringify(someArray)).

BONUS: kohandatud klasside eksemplari kopeerimine

Kui olete juba JavaScripti proff ja tegelete oma kohandatud konstruktori funktsioonide või klassidega, soovite võib-olla ka nende eksemplare kopeerida.

Nagu eespool mainitud, ei saa te neid lihtsalt stringida ja sõeluda, kuna kaotate oma klassi meetodid. Selle asemel soovite lisada copykõigi vanade väärtustega uue eksemplari loomiseks kohandatud meetodi. Vaatame, kuidas see töötab:

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

Teie eksemplaris viidatud objektide ja massiivide käsitlemiseks peate rakendama oma värskelt õpitud oskusi sügava kopeerimise kohta ! Lisan lihtsalt kohandatud konstruktori copymeetodi lõpliku lahenduse, et muuta see dünaamilisemaks:

Selle kopeerimismeetodi abil saate oma konstruktorisse panna nii palju väärtusi kui soovite, ilma et peaksite kõike käsitsi kopeerima!

Autori kohta: Lukas Gisder-Dubé asutas ja juhtis idufirmat CTO-na 1/2 aastat, luues tehnikameeskonna ja arhitektuuri. Pärast startupist lahkumist õpetas ta Ironhackis juhtiva juhendajana kodeerimist ja ehitab nüüd Berliini Startup Agency & Consultancy. Lisateabe saamiseks vaadake veebisaiti dube.io.