7 tips for å håndtere udefinert i JavaScript

De fleste moderne språk som Ruby, Python eller Java har en enkelt nullverdi (nil eller null), som virker en rimelig tilnærming.

Men JavaScript er annerledes.

null, men også undefined, representerer i JavaScript tomme verdier. Så hva er den eksakte forskjellen mellom dem?

Det korte svaret er at JavaScript-tolk returnerer undefined når du får tilgang til en variabel eller objektegenskap som ennå ikke er initialisert. For eksempel:

På den andre siden representerer null et manglende objekt referanse. JavaScript initialiserer ikke variabler eller objektegenskaper med null.

Noen innfødte metoder som String.prototype.match() kan returnere null for å betegne et manglende objekt. Ta en titt på eksemplet:

Fordi JavaScript er tillatt, har utviklere fristelsen til å få tilgang til ikke-initialiserte verdier. Jeg er skyldig i så dårlig praksis også.

Ofte genererer slike risikable handlinger undefined relaterte feil:

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

JavaScript-utvikler kan forstå ironien i denne vitsen :

For å redusere slike feil, må du forstå tilfellene når undefined genereres. La oss utforske undefined og effekten av kodesikkerheten.

1. Hva er udefinert

JavaScript har 6 primitive typer:

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

Fra 6 primitive typer er undefined en spesiell verdi med sin egen type Udefinert. I henhold til ECMAScript-spesifikasjonen:

Udefinert verdi, primitiv verdi brukes når en variabel ikke er tildelt en verdi.

Standarden definerer tydelig at du vil motta undefined når du får tilgang til ikke-initialiserte variabler, ikke-eksisterende objektegenskaper, ikke-eksisterende matriseelementer, og like.

Noen få eksempler:

Eksemplet ovenfor viser at tilgang:

  • en uinitialisert variabel number
  • en ikke-eksisterende objektegenskap movie.year
  • eller et ikke-eksisterende array-element movies

blir evaluert til undefined.

ECMAScript-spesifikasjonen definerer typen undefined verdi:

Udefinert type er en type hvis eneste verdi er undefined -verdien.

I denne forstand typeof operator returnerer "undefined" streng for en undefined -verdi:

Selvfølgelig fungerer typeof pent for å verifisere om en variabel inneholder en undefined -verdi:

2. Scenarier som skaper udefinert

2.1 Ikke-initialisert variabel

En erklært variabel, men som ennå ikke er tilordnet en verdi (ikke initialisert), er som standard undefined.

Enkelt og greit:

myVariable er erklært og ennå ikke tildelt en verdi. Å få tilgang til variabelen evalueres til undefined.

En effektiv tilnærming for å løse problemene med uinitialiserte variabler er når det er mulig tilordne en startverdi. Jo mindre variabelen eksisterer i en ikke-initialisert tilstand, jo bedre.

Ideelt sett tildeler du en verdi med en gang etter erklæring const myVariable = "Initial value". Men det er ikke alltid mulig.

Tips 1: Favoritt const, ellers bruk let, men si farvel til var

Etter min mening er en av de beste funksjonene i ECMAScript 2015 den nye måten å erklære variabler ved hjelp av const og let. Det er et stort skritt fremover.

const og let er blokkfelt (i motsetning til eldre funksjon scoped var) og eksisterer i en tidsmessig dødsone til erklæringslinjen.

Jeg anbefaler const variabel når verdien ikke kommer til å endres. Det skaper en uforanderlig binding.

En av de fine egenskapene til const er at du må tilordne en startverdi til variabelen const myVariable = "initial". Variabelen er ikke utsatt for den ikke-initialiserte tilstanden, og tilgang til undefined er umulig.

La oss sjekke funksjonen som verifiserer om et ord er palindrom:

length og half variabler tildeles en verdi en gang. Det virker rimelig å erklære dem som const siden disse variablene ikke kommer til å endres.

Bruk let -erklæring for variabler hvis verdi kan endres. Når det er mulig, tildeles en startverdi med en gang, f.eks. let index = 0.

Hva med den gamle skolen var? Mitt forslag er å slutte å bruke det.

var erklæringsproblem er variabelen som heiser innenfor funksjonsomfanget. Du kan erklære en var -variabel et sted på slutten av funksjonsomfanget, men likevel kan du få tilgang til den før erklæring: og du får en undefined.

myVariable er tilgjengelig og inneholder undefined allerede før erklæringslinjen: var myVariable = "Initial value".

Tvert imot, en const eller let -variabel kan ikke nås før erklæringslinjen – variabelen befinner seg i en tidsmessig dødsone før erklæringen. Og det er hyggelig fordi du har mindre sjanse til å få tilgang til en undefined.

Eksemplet ovenfor er oppdatert med let av var) kaster en ReferenceError fordi variabelen i den tidsmessige dødsonen ikke er tilgjengelig.

Oppmuntring til bruk av const for uforanderlige bindinger eller let ellers sikrer en praksis som reduserer utseendet til de ikke-initialiserte variabel.

Tips 2: Øk kohesjonen

Kohesjon karakteriserer i hvilken grad elementene i en modul (navneområde, klasse, metode, kodeblokk) hører sammen. Samholdet kan være høyt eller lavt.

En modul med høy kohesjon er å foretrekke fordi elementene i en slik modul kun fokuserer på en enkelt oppgave. Det gjør modulen:

  • Fokusert og forståelig: lettere å forstå hva modulen gjør
  • Vedlikeholdbar og lettere å refaktorere: endringen i modulen påvirker færre moduler
  • Gjenbrukbar: å være fokusert på en enkelt oppgave, det gjør modulen lettere å gjenbruke
  • Testbar: du vil lettere teste en modul som er fokusert på en enkelt oppgave

Høy kohesjon ledsaget av løs kobling er karakteristikken for et godt designet system.

En kodeblokk kan betraktes som en liten modul. For å dra nytte av fordelene med høy kohesjon, hold variablene så nær kodeblokken som bruker dem.

For eksempel, hvis en variabel bare eksisterer for å danne logikken for blokkomfang, så erklær og gjør variabelen levende bare innenfor den blokken (ved hjelp av const eller let erklæringer). Ikke utsett denne variabelen for det ytre blokkområdet, siden den ytre blokken ikke burde bry seg om denne variabelen.

Et klassisk eksempel på variabler som unødvendig forlenges, er bruken av for syklus inne i en funksjon:

index, item og length variabler blir deklarert i begynnelsen av funksjonslegemet. Imidlertid brukes de bare nær slutten. Hva er problemet med denne tilnærmingen?

Mellom erklæringen øverst og bruken i for uttalelse, variablene index, item er ikke initialisert og utsatt for undefined. De har en urimelig lang livssyklus i hele funksjonsomfanget.

En bedre tilnærming er å flytte disse variablene så nær bruksstedet som mulig:

index og item -variabler eksisterer bare i blokkeringsområdet for for -uttalelse. De har ingen betydning utenfor for.
length -variabelen blir også erklært nær kilden til bruken.

Hvorfor er den modifiserte versjonen bedre enn den opprinnelige? La oss se:

  • Variablene utsettes ikke for uinitialisert tilstand, og du har dermed ingen risiko for å få tilgang til undefined
  • Flytte variabler så nær bruksstedet som mulig øker kodelesbarheten
  • Høy kohesive koder er enklere å omformere og trekke ut i separate funksjoner, om nødvendig

2.2 Tilgang en ikke-eksisterende eiendom

Når du får tilgang til en ikke-eksisterende objektegenskap, returnerer JavaScript undefined.

La oss demonstrere det i et eksempel:

favoriteMovie er et objekt med en enkelt egenskap title. Å få tilgang til en ikke-eksisterende eiendom actors ved å bruke en eiendomstilgang favoriteMovie.actors evalueres til undefined.

Å få tilgang til en ikke-eksisterende eiendom kaster ikke feil. Problemet vises når vi prøver å få data fra den ikke-eksisterende eiendommen, som er den vanligste undefined -fellen, reflektert i den velkjente feilmeldingen TypeError: Cannot read property <prop> of undefined.

La oss endre forrige kodebit for å illustrere et TypeError kast:

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

Som et resultat kaster tilgang til det første elementet i en undefined -verdien med uttrykket favoriteMovie.actors en TypeError.

Den tillatende karakteren til JavaScript som gir tilgang til ikke-eksisterende eiendommer er en kilde til ikke-bestemthet: eiendommen kan være angitt eller ikke. Den gode måten å omgå dette problemet er å begrense objektet til alltid å ha definert egenskapene det inneholder.

Dessverre har du ofte ikke kontroll over objektene. Slike objekter kan ha et annet sett med egenskaper i forskjellige scenarier. Så du må håndtere alle disse scenariene manuelt.

La oss implementere en funksjon append(array, toAppend) som legger til i begynnelsen og / eller på slutten av en rekke nye elementer. toAppend -parameter godtar et objekt med egenskaper:

  • first: element satt inn i begynnelsen av array
  • last: element satt inn på slutten av array.

Funksjonen returnerer en ny matriseinstans, uten å endre den originale matrisen.

Den første versjonen av append(), litt naiv, kan se slik ut:

Fordi toAppend objekt kan utelate first eller last egenskaper, det er obligatorisk å verifisere om disse egenskapene finnes i toAppend.

En eiendomsadgang evalueres til undefined hvis egenskapen ikke eksisterer. Den første fristelsen til å sjekke om first eller last egenskaper er til stede, er å verifisere dem mot undefined. Dette utføres i betingede if(toAppend.first){} og if(toAppend.last){}

Ikke så raskt. Denne tilnærmingen har en ulempe. undefined, samt false, null, 0, NaN og "" er falske verdier.

I den nåværende implementeringen av append() tillater ikke funksjonen å sette inn falske elementer:

Tipsene som følger forklarer hvordan du korrekt kontrollerer eiendommens eksistens.

Tips 3: Kontroller eiendommens eksistens

Heldigvis tilbyr JavaScript en rekke måter å bestemme om objektet har en bestemt egenskap:

  • obj.prop !== undefined: sammenlign mot undefined direkte
  • typeof obj.prop !== "undefined": verifiser eiendomstypen
  • obj.hasOwnProperty("prop"): bekreft om objekt har en egen eiendom
  • "prop" in obj: verifiser om objektet har en egen eller arvet eiendom

Min anbefaling er å bruk operatøren in. Den har en kort og søt syntaks. in operatørtilstedeværelse antyder en klar hensikt om å sjekke om et objekt har en bestemt egenskap, uten å få tilgang til den faktiske eiendomsverdien.

obj.hasOwnProperty("prop") er også en fin løsning. Det er litt lengre enn in -operatøren og verifiserer bare i objektets egne egenskaper.

La oss forbedre append(array, toAppend) -funksjonen ved hjelp av in operator:

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

in operatør løser problemet med å sette inn falske elementer 0 og false. Når du nå legger til disse elementene i begynnelsen og slutten av , blir det forventede resultatet .

Tips 4: Destruksjon for å få tilgang til objektegenskaper

Når du får tilgang til en objektegenskap, er det noen ganger nødvendig å angi en standardverdi hvis egenskapen ikke eksisterer.

Du kan bruke in ledsaget av ternær operatør for å oppnå det:

Ternær operatørsyntaks blir skremmende når antall egenskaper å sjekke øker. For hver eiendom må du opprette en ny linje med kode for å håndtere standardene, og øke en stygg vegg med lignende utseende ternære operatører.

For å bruke en mer elegant tilnærming, la oss bli kjent med en flott ES2015-funksjon som kalles objektdestrukturering.

Objektdestruksjon tillater innebygd utvinning av verdier av objektegenskaper direkte i variabler og angir en standardverdi hvis egenskapen ikke eksisterer. En praktisk syntaks for å unngå å håndtere direkte med undefined.

Eiendomsutvinningen er nå presis:

For å se ting i aksjon, la oss definere en nyttig funksjon som bryter en streng i anførselstegn.

quote(subject, config) godtar det første argumentet som strengen som skal pakkes inn. Det andre argumentet config er et objekt med egenskapene:

Ved å bruke fordelene med objektdestruktureringen, la oss implementere quote():

const { char = """, skipIfQuoted = true } = config destruktureringsoppgave i en linje trekker ut egenskapene char og skipIfQuoted fra config -objekt.
Hvis noen egenskaper mangler i config -objektet, angir destruktureringsoppdraget standardverdiene : """ for char og false for skipIfQuoted.

Heldigvis har funksjonen fremdeles rom for forbedringer.

La oss flytte destruktureringsoppdraget til parameteravsnittet. Og angi en standardverdi (et tomt objekt { }) for config -parameteren, for å hoppe over det andre argumentet når standardinnstillingene er nok.

Destruktureringsoppdraget erstatter config -parameteren i funksjonens signatur. Jeg liker det: quote() blir en linje kortere.

= {} på høyre side av destruktureringsoppdraget sørger for at et tomt objekt brukes hvis det andre argumentet ikke er spesifisert i det hele tatt quote("Sunny day").

Objektdestruksjon er en kraftig funksjon som effektivt håndterer utvinning av egenskaper fra objekter. Jeg liker muligheten til å spesifisere en standardverdi som skal returneres når eiendommen du har tilgang til, ikke eksisterer. Som et resultat unngår du undefined og bryet rundt det.

Tips 5: Fyll objektet med standardegenskaper

Hvis det ikke er behov for å lage variabler for hver eiendom, slik destruktureringsoppdraget gjør, kan objektet som savner noen egenskaper fylles ut med standardverdier.

ES2015 Object.assign(target, source1, source2, ...) kopierer verdiene til alle opptellbare egne egenskaper fra ett eller flere kildeobjekter til målobjektet. Funksjonen returnerer målobjektet.

For eksempel må du få tilgang til egenskapene til unsafeOptions -objektet som ikke alltid inneholder hele settet med egenskaper.

For å unngå undefined når du får tilgang til en ikke-eksisterende eiendom fra unsafeOptions, la oss gjøre noen justeringer:

  • Definer et objekt defaults som inneholder standardverdiene for eiendommer
  • Ring Object.assign({ }, defaults, unsafeOptions) for å bygge et nytt objekt options. Det nye objektet mottar alle egenskaper fra unsafeOptions, men de manglende er hentet fra defaults.

unsafeOptions inneholder bare fontSize eiendom. defaults objekt definerer standardverdiene for egenskaper fontSize og color.

Object.assign() tar det første argumentet som et målobjekt {}. Målobjektet mottar verdien av fontSize -egenskap fra unsafeOptions kildeobjekt. Og verdien av color -egenskap fra defaults kildeobjekt, fordi unsafeOptions ikke inneholder color.

Rekkefølgen som kildeobjektene er oppført, betyr noe: senere kildeobjektegenskaper overskriver tidligere.

Du er nå trygg på å få tilgang til alle egenskapene til options -objektet, inkludert options.color som ikke var tilgjengelig i unsafeOptions i utgangspunktet.

Heldigvis finnes det et enklere alternativ for å fylle objektet med standardegenskaper.Jeg anbefaler å bruke spredningsegenskapene i objektinitialiserere.

I stedet for Object.assign() påkallelse, bruk objektspredningssyntaks for å kopiere til målobjektet alle egne og en-tallbare egenskaper fra kildeobjekter:

objektinitialiserer sprer egenskaper fra defaults og unsafeOptions kildeobjekter. Rekkefølgen som kildeobjektene er spesifisert i er viktig: egenskaper for senere kildeobjekter overskriver tidligere.

Å fylle et ufullstendig objekt med standard eiendomsverdier er en effektiv strategi for å gjøre koden din trygg og holdbar. Uansett situasjon, inneholder objektet alltid hele settet med egenskaper: og undefined kan ikke genereres.

Bonustips: nullish coalescing

Operatøren nullish coalescing evalueres til en standardverdi når operanden er undefined eller null:

Nullish coalescing operator er praktisk å få tilgang til en objektegenskap mens den har en standardverdi når denne egenskapen er undefined eller null:

styles objektet har ikke egenskapen color, og dermed styles.color egenskapstilgang er undefined. styles.color ?? "black" evalueres til standardverdien "black".

styles.fontSize er 18, så den nullaktive koalescerende operatøren evaluerer til eiendomsverdien 18.

2.3 Funksjonsparametere

Funksjonsparametrene er implisitt standard undefined.

Vanligvis skal en funksjon definert med et spesifikt antall parametere påkalles med samme antall argumenter. Det er da parametrene får verdiene du forventer:

When multiply(5, 3), parametrene a og b mottar 5 og henholdsvis 3 verdier. Multiplikasjonen beregnes som forventet: 5 * 3 = 15.

Hva skjer når du utelater et argument om påkallelse? Den tilsvarende parameteren inne i funksjonen blir undefined.

La oss endre forrige eksempel litt ved å kalle funksjonen med bare ett argument:

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

Tips 6: Bruk standardparameterverdi

Noen ganger krever en funksjon ikke hele settet med argumenter ved påkalling. Du kan angi standardverdier for parametere som ikke har noen verdi.

Når vi husker det forrige eksemplet, kan vi gjøre en forbedring. Hvis b -parameteren er undefined, la den være 2:

Funksjonen påkalles med et enkelt argument multiply(5). Opprinnelig er a parameter 2 og b er undefined.
Betinget utsagn verifiserer om b er undefined. Hvis det skjer, angir b = 2 -oppgave en standardverdi.

Selv om den angitte måten å tilordne standardverdier på fungerer, anbefaler jeg ikke å sammenligne direkte med undefined. Det er ordentlig og ser ut som et hack.

En bedre tilnærming er å bruke standardparameterfunksjonen ES2015. Det er kort, uttrykksfullt og ingen direkte sammenligninger med undefined.

Å legge til en standardverdi i parameteren b = 2 ser bedre ut:

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

ES2015s standardparameterfunksjon er intuitiv og uttrykksfull. Bruk den alltid til å angi standardverdier for valgfrie parametere.

2.4 Funksjonsreturverdi

Implisitt, uten return, en JavaScript-funksjon returnerer undefined.

En funksjon som ikke har return uttalelse returnerer implisitt undefined:

square() -funksjonen returnerer ingen beregningsresultater. Funksjonsinnkallingsresultatet er undefined.

Den samme situasjonen skjer når return uttalelse er til stede, men uten et uttrykk i nærheten:

return; uttalelse kjøres, men den gir ikke noe uttrykk. Påkallingsresultatet er også undefined.

Å indikere nær return fungerer selvfølgelig som forventet:

Nå blir funksjonsinnkallelsen evaluert til 4, som er 2 i kvadrat.

Tips 7: Ikke stol på den automatiske innsettingen av semikolon

Følgende liste med utsagn i JavaScript må ende med semikolon (;) :

  • tom uttalelse
  • let, const, var, import, export erklæringer
  • uttrykk uttalelse
  • debugger uttalelse
  • continue uttalelse, break uttalelse
  • throw uttalelse
  • return uttalelse

Hvis du bruker en av de ovennevnte utsagnene, sørg for å angi et semikolon på slutten:

På slutten av begge let erklæring og return uttalelse et obligatorisk semikolon skrives.

Hva skjer når du ikke vil indikere disse semikolon? I en slik situasjon gir ECMAScript en automatisk semikoloninnsetting (ASI) -mekanisme som setter inn de manglende semikolonene for deg.

Hjulpet av ASI kan du fjerne semikolonene fra forrige eksempel:

Teksten ovenfor er en gyldig JavaScript-kode. De manglende semikolonene settes automatisk inn for deg.

Ved første øyekast ser det ganske lovende ut. ASI-mekanismen lar deg hoppe over unødvendige semikolon. Du kan gjøre JavaScript-koden mindre og lettere å lese.

Det er en liten, men irriterende felle opprettet av ASI. Når en ny linje står mellom return og det returnerte uttrykket return \n expression, setter ASI automatisk inn et semikolon før den nye linjen return; \n expression.

Hva betyr det i en funksjon å ha return; uttalelse? Funksjonen returnerer undefined. Hvis du ikke kjenner i detalj mekanismen til ASI, er den uventet returnerte undefined misvisende.

La oss for eksempel studere den returnerte verdien av getPrimeNumbers() påkallelse:

Mellom return uttalelse og matrisen bokstavelig uttrykk eksisterer en ny linje. JavaScript setter automatisk inn et semikolon etter return, og tolker koden som følger:

Uttalelsen return; gjør funksjonen getPrimeNumbers() for å returnere undefined i stedet for den forventede matrisen.

Problemet løses ved å fjerne den nye linjen mellom return og bokstavelig matrise:

Min anbefaling er å studere hvordan nøyaktig automatisk innføring av semikolon fungerer for å unngå slike situasjoner.

Sett naturligvis aldri en ny linje mellom return og det returnerte uttrykket.

2,5 ugyldig operator

void <expression> evaluerer uttrykket og returnerer undefined uansett resultat av evalueringen.

En brukstilfelle av void operatør er å undertrykke uttrykksevaluering til undefined, avhengig av noen bivirkninger av evalueringen.

3. udefinert i matriser

Du får undefined når du får tilgang til et arrayelement med en indeks utenfor grensene.

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

Fordi det ikke er matriseelementer ved indekser 5 og -1, er tilgangsdelene colors og colors er undefined.

I JavaScript kan du støte på såkalte sparsomme matriser. Teser er matriser som har hull, dvs. ved noen indekser er ingen elementer definert.

Når du får tilgang til et gap (også kjent som tomt spor) i et sparsomt utvalg, får du også en undefined.

Følgende eksempel genererer sparsomme matriser og prøver å få tilgang til de tomme sporene deres:

sparse1 er opprettet ved å påkalle en Array konstruktør med et numerisk første argument.Den har 3 tomme spor.

sparse2 er opprettet med en matrise bokstavelig med det manglende andre elementet.

I noen av disse sparsomme matriser evalueres tilgang til et tomt spor til undefined.

Når du arbeider med matriser, for å unngå undefined, må du bruke gyldige matriseindekser og forhindre at det oppstår sparsomme matriser.

4. Forskjell mellom udefinert og null

Hva er hovedforskjellen mellom undefined og null? Begge spesialverdiene innebærer en tom tilstand.

undefined representerer verdien av en variabel som ennå ikke er initialisert, mens null representerer et forsettlig fravær av et objekt.

La oss utforske forskjellen i noen eksempler.

Variabelen number er definert tildeles imidlertid ikke en opprinnelig verdi:

number variabel er undefined, som indikerer en uinitialisert variabel.

Det samme uinitialiserte konseptet skjer når en ikke-eksisterende objektegenskap er tilgjengelig:

Fordi lastName egenskapen eksisterer ikke i obj, JavaScript vurderer obj.lastName til undefined.

På den andre siden vet du at en variabel forventer et objekt. Men av en eller annen grunn kan du ikke instantiere objektet. I så fall er null en meningsfull indikator for et manglende objekt.

For eksempel er clone() en funksjon som kloner et vanlig JavaScript-objekt. Funksjonen forventes å returnere et objekt:

Imidlertid kan clone() påberopes med et ikke-objekt-argument: 15 eller null. I et slikt tilfelle kan ikke funksjonen opprette en klon, så den returnerer null – indikatoren for et manglende objekt.

typeof operator skiller mellom undefined og null:

Også den strenge kvalitetsoperatøren === skiller riktig undefined fra null:

5. Konklusjon

undefined eksistens er en konsekvens av JavaScripts tillatende natur som tillater bruk av:

  • uinitialiserte variabler
  • ikke-eksisterende objektegenskaper eller -metoder
  • indekser utenfor grensene for å få tilgang til matriseelementer
  • påkallingsresultatet til en funksjon som ikke gir noe

Å sammenligne direkte mot undefined er usikkert fordi du stoler på en tillatt, men motløs praksis som er nevnt ovenfor.

En effektiv strategi er å minimere utseendet til undefined søkeord i koden din ved å bruke gode vaner som:

  • reduser bruken av ikke-initialiserte variabler
  • gjør variablene livssyklus korte og nær kilden til deres bruk
  • når det er mulig tilordne startverdier til variabler
  • favoriser const, ellers bruk let
  • bruk standardverdier for ubetydelige funksjonsparametere
  • bekreft egenskapene eksistens eller fyll de usikre objektene med standardegenskaper
  • unngå bruk av sparsomme matriser

Er det bra at JavaScript har både undefined og null for å representere tomme verdier?

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *