7 tip til håndtering udefineret i JavaScript

De fleste moderne sprog som Ruby, Python eller Java har en enkelt nulværdi (nil eller null), hvilket synes at være en rimelig tilgang.

Men JavaScript er anderledes.

null, men også undefined, repræsenterer i JavaScript tomme værdier. Så hvad er den nøjagtige forskel mellem dem?

Det korte svar er, at JavaScript-tolk returnerer undefined, når du får adgang til en variabel- eller objektegenskab, der endnu ikke er initialiseret. For eksempel:

På den anden side repræsenterer null et manglende objekt reference. JavaScript initialiserer ikke variabler eller objektegenskaber med null.

Nogle native metoder som String.prototype.match() kan returnere null for at betegne et manglende objekt. Se på eksemplet:

Fordi JavaScript er tilladt, har udviklere fristelsen til at få adgang til ikke-initialiserede værdier. Jeg er også skyldig i sådan dårlig praksis.

Ofte genererer sådanne risikable handlinger undefined relaterede fejl:

  • TypeError: "undefined" is not a function
  • TypeError: Cannot read property "<prop-name>" of undefined
  • og lignende typefejl.

JavaScript-udvikler kan forstå ironien i denne vittighed :

For at reducere sådanne fejl skal du forstå de tilfælde, hvor undefined genereres. Lad os undersøge undefined og dens virkning på kodesikkerhed.

1. Hvad der er udefineret

JavaScript har 6 primitive typer:

Og en adskilt objekttype: {name: "Dmitri"} , .

Fra 6 primitive typer er undefined en speciel værdi med sin egen type Udefineret. I henhold til ECMAScript-specifikation:

Udefineret værdi primitiv værdi bruges, når en variabel ikke er tildelt en værdi.

Standarden definerer klart, at du vil modtage undefined, når du får adgang til ikke-initialiserede variabler, ikke-eksisterende objektegenskaber, ikke-eksisterende matrixelementer og lignende.

Et par eksempler:

Ovenstående eksempel viser, at adgang til:

  • en uinitialiseret variabel number
  • en ikke-eksisterende objektegenskab movie.year
  • eller et ikke-eksisterende array-element movies

evalueres til undefined.

ECMAScript-specifikationen definerer typen undefined værdi:

Udefineret type er en type, hvis eneste værdi er undefined -værdien.

I denne forstand typeof operator returnerer "undefined" streng for en undefined værdi:

Selvfølgelig fungerer typeof pænt for at kontrollere, om en variabel indeholder en undefined -værdi:

2. Scenarier, der skaber udefineret

2.1 Ikke-initialiseret variabel

En erklæret variabel, men endnu ikke tildelt en værdi (ikke-initialiseret) er som standard undefined.

Almindeligt og simpelt:

myVariable erklæres og endnu ikke tildeles en værdi. Adgang til variablen evalueres til undefined.

En effektiv tilgang til løsning af problemer med ikke-initialiserede variabler tildeles, når det er muligt, en startværdi. Jo mindre variablen eksisterer i en uinitialiseret tilstand, jo bedre.

Ideelt set tildeler du en værdi med det samme efter erklæring const myVariable = "Initial value". Men det er ikke altid muligt.

Tip 1: Favorit const, ellers brug let, men farvel til var

Efter min mening er en af de bedste funktioner i ECMAScript 2015 den nye måde at erklære variabler ved hjælp af const og let. Det er et stort skridt fremad.

const og let er blokomfanget (i modsætning til ældre funktion scoped var) og eksisterer i en tidsmæssig død zone indtil deklarationslinjen.

Jeg anbefaler const variabel, når dens værdi ikke vil ændre sig. Det skaber en uforanderlig binding.

Et af de gode træk ved const er, at du skal tildele en startværdi til variablen const myVariable = "initial". Variablen udsættes ikke for den ikke-initialiserede tilstand, og adgang til undefined er umulig.

Lad os kontrollere den funktion, der verificerer, om et ord er et palindrom:

length og half variabler tildeles en værdi en gang. Det forekommer rimeligt at erklære dem som const, da disse variabler ikke vil ændre sig.

Brug let -erklæring til variabler, hvis værdi kan ændres. Når det er muligt, tildeles en startværdi med det samme, f.eks. let index = 0.

Hvad med den gamle skole var? Mit forslag er at stoppe med at bruge det.

var erklæringsproblem er den variable hejsning inden for funktionsomfanget. Du kan erklære en var variabel et sted i slutningen af funktionsomfanget, men alligevel kan du få adgang til den før erklæring: og du får en undefined.

myVariable er tilgængelig og indeholder undefined allerede før erklæringslinjen: var myVariable = "Initial value".

I modsætning hertil er en const eller let variabel ikke tilgængelig før erklæringslinjen – variablen befinder sig i en tidsmæssig død zone før erklæringen. Og det er pænt, fordi du har mindre chance for at få adgang til en undefined.

Ovenstående eksempel opdateret med let af var) kaster en ReferenceError fordi variablen i den tidsmæssige døde zone ikke er tilgængelig.

Tilskyndelse til brugen af const til uforanderlige bindinger eller let ellers sikrer en praksis, der reducerer udseendet på de ikke-initialiserede variabel.

Tip 2: Forøg samhørighed

Samhørighed karakteriserer den grad, som elementerne i et modul (navneområde, klasse, metode, kodeblok) hører sammen. Samhørigheden kan være høj eller lav.

Et modul med høj samhørighed foretrækkes, fordi elementerne i et sådant modul kun fokuserer på en enkelt opgave. Det gør modulet:

  • Fokuseret og forståeligt: lettere at forstå, hvad modulet gør
  • Vedligeholdeligt og lettere at reflektere: ændringen i modulet påvirker færre moduler
  • Genanvendelig: at være fokuseret på en enkelt opgave, det gør modulet lettere at genbruge
  • Testbart: du ville lettere teste et modul, der er fokuseret på en enkelt opgave

Høj samhørighed ledsaget af løs kobling er karakteristikken for et veldesignet system.

En kodeblok kan betragtes som et lille modul. For at drage fordel af fordelene ved høj samhørighed skal du holde variablerne så tæt som muligt på den kodeblok, der bruger dem.

For eksempel, hvis en variabel kun eksisterer for at danne logikken for blokområdet, skal du kun erklære og gøre variablen levende inden for den blok (ved hjælp af const eller let erklæringer). Udsæt ikke denne variabel for det ydre blokomfang, da den ydre blok ikke burde bekymre sig om denne variabel.

Et klassisk eksempel på variablenes unødigt forlængede levetid er brugen af for cyklus inde i en funktion:

index, item og length variabler erklæres i begyndelsen af funktionslegemet. De bruges dog kun nær slutningen. Hvad er problemet med denne tilgang?

Mellem erklæringen øverst og brugen i for udsagn variablerne index, item er ikke-initialiseret og udsat for undefined. De har en urimelig lang livscyklus i hele funktionsomfanget.

En bedre tilgang er at flytte disse variabler så tæt som muligt på deres brugssted:

index og item variabler findes kun i blokområdet for for udsagn. De har ingen betydning uden for for.
length -variablen erklæres også tæt på dens anvendelseskilde.

Hvorfor er den ændrede version bedre end den oprindelige? Lad os se:

  • Variablerne udsættes ikke for uinitialiseret tilstand, så du har ingen risiko for at få adgang til undefined
  • Flytning af variabler så tæt som muligt på deres brugssted øger kodelæsbarheden
  • Høje sammenhængende klumper af kode er lettere at omformulere og udtrække i separate funktioner, om nødvendigt

2.2 Adgang en ikke-eksisterende ejendom

Når du får adgang til en ikke-eksisterende objektegenskab, returnerer JavaScript undefined.

Lad os demonstrere det i et eksempel:

favoriteMovie er et objekt med en enkelt egenskab title. Adgang til en ikke-eksisterende ejendom actors ved hjælp af en ejendomsadgang favoriteMovie.actors evalueres til undefined.

Adgang til en ikke-eksisterende ejendom kaster ikke en fejl. Problemet vises, når man prøver at hente data fra den ikke-eksisterende ejendom, som er den mest almindelige undefined fælde, hvilket afspejles i den velkendte fejlmeddelelse TypeError: Cannot read property <prop> of undefined.

Lad os ændre det forrige kodestykke lidt for at illustrere et TypeError kast:

favoriteMovie har ikke egenskaben actors, så favoriteMovie.actors evalueres til undefined.

Som et resultat kaster adgang til det første element i en undefined -værdi ved hjælp af udtrykket favoriteMovie.actors et TypeError.

Den tilladende karakter af JavaScript, der giver adgang til ikke-eksisterende ejendomme, er en kilde til ikke-bestemmelse: ejendommen kan være indstillet eller ej. Den gode måde at omgå dette problem er at begrænse objektet til altid at have defineret de egenskaber, det har.

Desværre har du ofte ikke kontrol over objekterne. Sådanne objekter kan have et andet sæt egenskaber i forskellige scenarier. Så du skal håndtere alle disse scenarier manuelt.

Lad os implementere en funktion append(array, toAppend), der tilføjer i begyndelsen og / eller i slutningen af en række nye elementer. toAppend parameter accepterer et objekt med egenskaber:

  • first: element indsat i begyndelsen af array
  • last: element indsat i slutningen af array.

Funktionen returnerer en ny matrixforekomst uden at ændre den oprindelige matrix.

Den første version af append(), lidt naiv, kan se sådan ud:

Fordi toAppend objekt kan udelade first eller last egenskaber, det er obligatorisk at kontrollere, om disse egenskaber findes i toAppend.

En ejendomsadgang evalueres til undefined, hvis ejendommen ikke findes. Den første fristelse til at kontrollere, om first eller last egenskaber er til stede, er at kontrollere dem mod undefined. Dette udføres i betingede if(toAppend.first){} og if(toAppend.last){}

Ikke så hurtigt. Denne tilgang har en ulempe. undefined samt false, null, 0, NaN og "" er falske værdier.

I den aktuelle implementering af append() tillader funktionen ikke at indsætte falske elementer:

Tipene, der følger, forklarer, hvordan man korrekt kontrollerer ejendommens eksistens.

Tip 3: Kontroller ejendommens eksistens

Heldigvis tilbyder JavaScript en række måder at afgøre, om objektet har en bestemt egenskab:

  • obj.prop !== undefined: sammenlign med undefined direkte
  • typeof obj.prop !== "undefined": Kontroller egenskabstypen
  • obj.hasOwnProperty("prop"): Kontroller, om objekt har en egen egenskab
  • "prop" in obj: kontroller, om objektet har en egen eller arvet ejendom

Min anbefaling er at brug operatøren in. Det har en kort og sød syntaks. in operatørtilstedeværelse antyder en klar hensigt om at kontrollere, om et objekt har en bestemt egenskab uden adgang til den aktuelle ejendomsværdi.

obj.hasOwnProperty("prop") er også en god løsning. Det er lidt længere end in -operatøren og verificerer kun i objektets egne egenskaber.

Lad os forbedre append(array, toAppend) -funktionen ved hjælp af in operator:

"first" in toAppend (og "last" in toAppend) er true om den tilsvarende egenskab findes, false ellers.

in operatør løser problemet med indsættelse af falske elementer 0 og false. Tilføjelse af disse elementer i begyndelsen og slutningen af giver nu det forventede resultat .

Tip 4: Destrukturering for at få adgang til objektegenskaber

Når du får adgang til en objektegenskab, er det undertiden nødvendigt at indstille en standardværdi, hvis ejendommen ikke findes.

Du kan muligvis bruge in ledsaget af ternær operatør til at opnå dette:

Ternær operatørsyntaks bliver skræmmende, når antallet af egenskaber, der skal kontrolleres, øges. For hver ejendom skal du oprette en ny linje kode for at håndtere standardindstillingerne og øge en grim mur af lignende udseende ternære operatører.

For at bruge en mere elegant tilgang, lad os blive fortrolig med en fantastisk ES2015-funktion kaldet objektdestrukturering.

Objektdestrukturering muliggør indbygget ekstraktion af objektegenskabsværdier direkte i variabler og indstilling af en standardværdi, hvis ejendommen ikke findes. En bekvem syntaks for at undgå at beskæftige sig direkte med undefined.

Faktisk er ejendomsudvindingen nu præcis:

For at se ting i aktion, lad os definere en nyttig funktion, der omslutter en streng i anførselstegn.

quote(subject, config) accepterer det første argument som den streng, der skal pakkes ind. Det andet argument config er et objekt med egenskaberne:

Ved at anvende fordelene ved objektdestruktureringen, lad os implementere quote():

const { char = """, skipIfQuoted = true } = config destruktionstildeling i en linje udtrækker egenskaberne char og skipIfQuoted fra config -objekt.
Hvis der mangler nogle egenskaber i config -objektet, indstiller destruktionstildelingen standardværdierne : """ til char og false til skipIfQuoted.

Heldigvis har funktionen stadig plads til forbedringer.

Lad os flytte destruktionstildelingen til parameterafsnittet. Og indstil en standardværdi (et tomt objekt { }) til config -parameteren for at springe det andet argument over, når standardindstillingerne er tilstrækkelige.

Destruktureringsopgaven erstatter parameteren config i funktionens signatur. Det kan jeg godt lide: quote() bliver en linje kortere.

= {} på højre side af destruktureringsopgaven sikrer, at et tomt objekt bruges, hvis det andet argument overhovedet ikke er angivet quote("Sunny day").

Objektdestrukturering er en kraftfuld funktion, der effektivt håndterer udvinding af egenskaber fra objekter. Jeg kan godt lide muligheden for at angive en standardværdi, der skal returneres, når den ejendom, der er adgang til, ikke findes. Som et resultat undgår du undefined og besværet omkring det.

Tip 5: Fyld objektet med standardegenskaber

Hvis der ikke er behov for at oprette variabler for hver ejendom, som destruktionstildelingen gør, kan objektet, der savner nogle egenskaber, udfyldes med standardværdier.

ES2015 Object.assign(target, source1, source2, ...) kopierer værdierne for alle enerable egne egenskaber fra et eller flere kildeobjekter til målobjektet. Funktionen returnerer målobjektet.

For eksempel skal du få adgang til egenskaberne for unsafeOptions objekt, der ikke altid indeholder dets fulde sæt egenskaber.

For at undgå undefined når du får adgang til en ikke-eksisterende ejendom fra unsafeOptions, lad os foretage nogle justeringer:

  • Definer et objekt defaults, der indeholder standardværdierne for egenskaber
  • Ring til Object.assign({ }, defaults, unsafeOptions) et nyt objekt options. Det nye objekt modtager alle egenskaber fra unsafeOptions, men de manglende er taget fra defaults.

unsafeOptions indeholder kun fontSize egenskab. defaults objekt definerer standardværdierne for egenskaber fontSize og color.

Object.assign() tager det første argument som et målobjekt {}. Målobjektet modtager værdien af fontSize ejendom fra unsafeOptions kildeobjekt. Og værdien af color egenskab fra defaults kildeobjekt, fordi unsafeOptions ikke indeholder color.

Den rækkefølge, som kildeobjekterne tælles i, betyder noget: senere kildeobjektegenskaber overskriver tidligere.

Du er nu sikker på at få adgang til enhver ejendom af options objekt, inklusive options.color, der ikke var tilgængelig i unsafeOptions oprindeligt.

Heldigvis findes der et lettere alternativ til at udfylde objektet med standardegenskaber.Jeg anbefaler at bruge spredningsegenskaberne i objektinitialiserere.

I stedet for Object.assign() påkaldelse skal du bruge objektspredningssyntaxen til at kopiere til målobjektet alle egne og en række egenskaber fra kildeobjekter:

objekt initializer spreder egenskaber fra defaults og unsafeOptions kildeobjekter. Den rækkefølge, som kildeobjekterne er specificeret i, er vigtig: egenskaber for senere kildeobjekter overskriver tidligere.

Udfyldning af et ufuldstændigt objekt med standardegenskabsværdier er en effektiv strategi for at gøre din kode sikker og holdbar. Uanset situationen indeholder objektet altid det fulde sæt egenskaber: og undefined kan ikke genereres.

Bonustip: nullish coalescing

Operatøren nullish coalescing evalueres til en standardværdi, når dens operand er undefined eller null:

Nullish coalescing operator er praktisk at få adgang til en objektegenskab, mens den har en standardværdi, når denne egenskab er undefined eller null:

styles objekt har ikke ejendommen color, så styles.color ejendomsadgang er undefined. styles.color ?? "black" evalueres til standardværdien "black".

styles.fontSize er 18, så den nullish coalescing-operatør evaluerer til egenskabsværdien 18.

2.3 Funktionsparametre

Funktionsparametrene er implicit standard til undefined.

Normalt skal en funktion defineret med et specifikt antal parametre påberåbes med det samme antal argumenter. Det er her, parametrene får de værdier, du forventer:

When multiply(5, 3), parametrene a og b modtager 5 og henholdsvis 3 værdier. Multiplikationen beregnes som forventet: 5 * 3 = 15.

Hvad sker der, når du udelader et argument om påkaldelse? Den tilsvarende parameter inde i funktionen bliver undefined.

Lad os ændre det foregående eksempel lidt ved at kalde funktionen med kun et argument:

Påkaldelsen multiply(5) udføres med et enkelt argument: som resultat a parameter er 5 b parameter er undefined.

Tip 6: Brug standardparameterværdi

Nogle gange kræver en funktion ikke det fulde sæt argumenter ved påkaldelse. Du kan angive standardindstillinger for parametre, der ikke har en værdi.

Når vi husker det foregående eksempel, skal vi gøre en forbedring. Hvis b -parameteren er undefined, skal du standardindstille den til 2:

Funktionen påberåbes med et enkelt argument multiply(5). Oprindeligt er a parameter 2 og b er undefined.
Den betingede erklæring verificerer, om b er undefined. Hvis det sker, indstiller b = 2 -tildelingen en standardværdi.

Mens den angivne måde at tildele standardværdier fungerer på, anbefaler jeg ikke at sammenligne direkte med undefined. Det er ordentligt og ligner et hack.

En bedre tilgang er at bruge ES2015-standardparametre-funktionen. Det er kort, udtryksfuldt og ingen direkte sammenligning med undefined.

Tilføjelse af en standardværdi til parameter b = 2 ser bedre ud:

b = 2 i funktionssignaturen sørger for at hvis b er undefined, parameteren er som standard 2.

ES2015s standardparameterfunktion er intuitiv og udtryksfuld. Brug den altid til at indstille standardværdier for valgfri parametre.

2.4 Funktionsreturværdi

Implicit, uden return udsagn, en JavaScript-funktion returnerer undefined.

En funktion, der ikke har return sætning returnerer implicit undefined:

square() -funktionen returnerer ingen beregningsresultater. Funktionsopkaldsresultatet er undefined.

Den samme situation sker, når return udsagn er til stede, men uden et udtryk i nærheden:

return; udsagn udføres, men den returnerer ikke noget udtryk. Påkaldsresultatet er også undefined.

Når det angives nær return, fungerer det udtryk, der skal returneres, naturligvis som forventet:

Funktionsopkald evalueres til 4, som er 2 kvadratisk.

Tip 7: Stol ikke på den automatiske indsættelse af semikolon

Følgende sætningsliste i JavaScript skal slutte med semikolon (;) :

  • tom erklæring
  • let, const, var, import, export erklæringer
  • udtrykserklæring
  • debugger udsagn
  • continue udsagn, break udsagn
  • throw udsagn
  • return udsagn

Hvis du bruger et af ovenstående udsagn, skal du angive et semikolon i slutningen:

I slutningen af begge let erklæring og return udsagn der skrives et obligatorisk semikolon.

Hvad sker der, når du ikke vil angive disse semikoloner? I en sådan situation giver ECMAScript en automatisk semikolonindsættelsesmekanisme (ASI), der indsætter de manglende semikoloner til dig.

Hjælpet af ASI kan du fjerne semikolonerne fra det foregående eksempel:

Ovenstående tekst er en gyldig JavaScript-kode. De manglende semikoloner indsættes automatisk for dig.

Ved første øjekast ser det ret lovende ud. ASI-mekanismen giver dig mulighed for at springe de unødvendige semikoloner over. Du kan gøre JavaScript-koden mindre og lettere at læse.

Der er en lille, men irriterende fælde oprettet af ASI. Når en ny linje står mellem return og det returnerede udtryk return \n expression, indsætter ASI automatisk et semikolon før den nye linje return; \n expression.

Hvad betyder det inde i en funktion at have return; udsagn? Funktionen returnerer undefined. Hvis du ikke kender i detaljer mekanismen for ASI, er den uventet returnerede undefined vildledende.

Lad os for eksempel studere den returnerede værdi af getPrimeNumbers() påkaldelse:

Mellem return udsagn og arrayets bogstavelige udtryk findes en ny linje. JavaScript indsætter automatisk et semikolon efter return og fortolker koden som følger:

Påstanden return; gør funktionen getPrimeNumbers() til at returnere undefined i stedet for det forventede array.

Problemet løses ved at fjerne den nye linje mellem return og litterær array:

Min anbefaling er at undersøge, hvordan præcis automatisk semikolonindsættelse fungerer for at undgå sådanne situationer.

Sæt naturligvis aldrig en ny linje mellem return og det returnerede udtryk.

2,5 ugyldig operator

void <expression> evaluerer udtrykket og returnerer undefined uanset resultatet af evalueringen.

En brugstilfælde af void operatør er at undertrykke ekspressionsevaluering til undefined, afhængig af en eller anden bivirkning af evalueringen.

3. udefineret i arrays

Du får undefined, når du får adgang til et array-element med et indeks uden for grænser.

colors array har 3 elementer, så gyldige indekser er 0, 1 og 2.

Da der ikke er nogen matrixelementer ved indekser 5 og -1, er accessorerne colors og colors er undefined.

I JavaScript kan du støde på såkaldte sparsomme arrays. Specialer er arrays, der har huller, dvs. ved nogle indekser er der ikke defineret nogen elementer.

Når der åbnes et hul (også kendt som tom plads) i et sparsomt array, får du også en undefined.

Følgende eksempel genererer sparsomme arrays og forsøger at få adgang til deres tomme slots:

sparse1 oprettes ved at påkalde en Array konstruktør med et numerisk første argument.Den har 3 tomme slots.

sparse2 oprettes med en matrix bogstavelig med det manglende andet element.

I nogen af disse sparsomme arrays evalueres adgang til en tom plads til undefined.

Når du arbejder med arrays, skal du sørge for at bruge gyldige array-indekser og forhindre oprettelse af sparsomme arrays for at undgå undefined.

4. Forskel mellem udefineret og null

Hvad er den største forskel mellem undefined og null? Begge specielle værdier indebærer en tom tilstand.

undefined repræsenterer værdien af en variabel, der endnu ikke er initialiseret, mens null repræsenterer et forsætligt fravær af et objekt.

Lad os undersøge forskellen i nogle eksempler.

Variablen number er defineret tildeles dog ikke en startværdi:

number variabel er undefined, hvilket indikerer en ikke-initialiseret variabel.

Det samme uinitialiserede koncept sker, når der er adgang til en ikke-eksisterende objektegenskab:

Fordi lastName egenskab findes ikke i obj, JavaScript evaluerer obj.lastName til undefined.

På den anden side ved du, at en variabel forventer et objekt. Men af en eller anden grund kan du ikke instantiere objektet. I et sådant tilfælde er null en meningsfuld indikator for et manglende objekt.

For eksempel er clone() en funktion, der kloner et almindeligt JavaScript-objekt. Funktionen forventes at returnere et objekt:

Dog kan clone() påberåbes med et ikke-objekt-argument: 15 eller null. I et sådant tilfælde kan funktionen ikke oprette en klon, så den returnerer null – indikatoren for et manglende objekt.

typeof operator skelner mellem undefined og null:

Også den strenge kvalitetsoperator === skelner korrekt undefined fra null:

5. Konklusion

undefined eksistens er en konsekvens af JavaScripts tilladende natur, der tillader brug af:

  • ikke-initialiserede variabler
  • ikke-eksisterende objektegenskaber eller -metoder
  • indekser uden for grænser for at få adgang til matrixelementer
  • påkaldsresultatet af en funktion, der ikke returnerer noget

At sammenligne direkte med undefined er ikke sikkert, fordi du stoler på en tilladt, men afskrækket praksis, der er nævnt ovenfor.

En effektiv strategi er at minimere udseendet af undefined søgeord i din kode ved at anvende gode vaner som:

  • reducere brugen af ikke-initialiserede variabler
  • gøre variablerne livscyklus korte og tæt på kilden til deres brug
  • når det er muligt tildele startværdier til variabler
  • favoriser const, ellers brug let
  • brug standardværdier til ubetydelige funktionsparametre
  • verificer egenskaberne eksistens eller udfyld de usikre objekter med standardegenskaber
  • undgå brugen af sparsomme arrays

Er det godt, at JavaScript har både undefined og null for at repræsentere tomme værdier?

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *