[ Web Proxy ]
URL:
Viewing: https://da.javascript.info/function-object [Back]  [Original]

Funktion som objekt, NFE
DA

Vi nsker at gre dette open source-projekt tilgngeligt for folk over hele verden.

Hjlp med at overstte indholdet af denne tutorial til dit sprog!

    Sg p Javascript.info:
    Sg i tutorialen:
    Lyst temaMrkt tema
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Som vi allerede har set er en funktion i JavaScript en vrdi.

    Alle vrdier i JavaScript har en type. Hvad type har en funktion?

    I JavaScript er funktioner objekter.

    En god mde at forestille sig funktioner er som kaldbare handlingsobjekter. Vi kan ikke kun kalde dem, men ogs behandle dem som objekter: tilfje/fjerne egenskaber, videregive som reference osv.

    Egenskaben name

    Funktionens objekt indeholder nogle brugbare egenskaber.

    For eksempel er en funktions navn tilgngeligt som egenskaben name:

    function sayHi() {
      alert("Hej);
    }
    
    alert(sayHi.name); // sayHi

    Det er lidt pudsigt, men tildeling af navne er smart. Det tildeler ogs det korrekte navn til en funktion, selv hvis den oprettes uden et navn og derefter jeblikkeligt tildeles til en variabel:

    let sayHi = function() {
      alert("Hej");
    };
    
    alert(sayHi.name); // sayHi (den har fet et navn!)

    Det virker ogs hvis tildelingen er sket via en standardvrdi:

    function f(sayHi = function() {}) {
      alert(sayHi.name); // sayHi (virker!)
    }
    
    f();

    I specifikationen kaldes denne funktion contextual name. Hvis funktionen ikke har et navn, s bliver det tildelt et fra konteksten.

    Objektets metoder har ogs navne, som er deres ngle i objektet:

    let user = {
    
      sayHi() {
        // ...
      },
    
      sayBye: function() {
        // ...
      }
    
    }
    
    alert(user.sayHi.name); // sayHi
    alert(user.sayBye.name); // sayBye

    Der skabes dog ikke navne ud af den bl luft. Der er situationer hvor det er umuligt at finde det korrekte navn. I sdanne tilflde er name-egenskaben tom, som her:

    // funktion oprettet inde i et array
    let arr = [function() {}];
    
    alert( arr[0].name ); // <empty string>
    // motoren har ingen mde at finde det korrekte navn, s den fr ikke noget

    I praksis vil langt de fleste funktioner dog have et navn.

    Egenskaben length

    Der er en anden indbygget egenskab length som returnerer antallet af funktionsparametre, for eksempel:

    function f1(a) {}
    function f2(a, b) {}
    function many(a, b, ...more) {}
    
    alert(f1.length); // 1
    alert(f2.length); // 2
    alert(many.length); // 2

    Som vi kan se ovenfor s bliver rest parameteren ikke talt med.

    Egenskaben length bruges nogle gange til introspection i funktioner der opererer p andre funktioner.

    For eksempel vil koden nedenfor vil funktionen ask acceptere et question der skal sprges efter og et antal handler funktioner der kan kaldes.

    S snart brugeren giver deres svar vil funktionen kalde de leverede handlers. Vi kan levere to slags handlers:

    • En funktion uden argumenter, som kun kaldes nr brugeren giver et positivt svar.
    • En funktion med argumenter, som kaldes i begge tilflde og returnerer et svar.

    For at kalde handler p den rigtige mde undersger vi egenskaben handler.length.

    Iden er at vi har en simpel syntaks uden argumenter for positive svar (mest almindeligt), men ogs er i stand til at understtte universelle handlers der kan hndtere bde positive og negative svar, hvis det er ndvendigt.:

    function ask(question, ...handlers) {
      let isYes = confirm(question);
    
      for(let handler of handlers) {
        if (handler.length == 0) {
          if (isYes) handler();
        } else {
          handler(isYes);
        }
      }
    
    }
    
    // for positive svar, begge handlers kaldes
    // for negative svar, kun den anden
    ask("Sprgsml?", () => alert('Du sagde ja'), result => alert(result));

    Dette er et srligt mnster i programmering kaldet polymorphism der behandler argumenter forskelligt alt efter type i vores tilflde afhngigt af length. Metoden ses ofte brugt i JavaScript-biblioteker.

    Brugerdefinerede egenskaber

    Vi kan ogs tilfje vores egne egenskaber.

    Her tilfjer vi egenskaben counter for at spore antallet af kald:

    function sayHi() {
      alert("Hej");
    
      // lad os tlle hvor mange gange vi kaldes
      sayHi.counter++;
    }
    sayHi.counter = 0; // startvrdi
    
    sayHi(); // Hej
    sayHi(); // Hej
    
    alert( `Kaldt ${sayHi.counter} gang(e)` ); // Kaldt 2 gang(e)
    En egenskab er ikke en variabel

    En egenskab tildelt til en funktion som sayHi.counter = 0 definerer ikke en lokal variabel counter inde i den. Med andre ord, egenskaben counter og en variabel let counter er to forskellige ting.

    Vi kan behandle en funktion som et objekt, gemme egenskaber i den, men det har ingen effekt p dens udfrelse. Lokale variable er ikke funktionsegenskaber og omvendt. Disse er to parallelle verdener.

    Egenskaber i funktioner kan nogle gange erstatte closures. For eksempel kan vi omskrive counter-funktionen fra kapitlet Variable scope, closure til at bruge en funktionsegenskab i stedet for en variabel i en closure:

    function makeCounter() {
      // i stedet for:
      // let count = 0
    
      function counter() {
        return counter.count++;
      };
    
      counter.count = 0;
    
      return counter;
    }
    
    let counter = makeCounter();
    alert( counter() ); // 0
    alert( counter() ); // 1

    count er nu gemt direkte i funktionen, ikke i dens ydre leksikale milj.

    Er det bedre eller vrre end at bruge en closure?

    Den vigtigste forskel er at hvis vrdien af count lever i en ydre variabel, s kan ekstern kode ikke tilg den kun indre funktioner kan ndre den. Og hvis den er bundet til en funktion, s er det muligt:

    function makeCounter() {
    
      function counter() {
        return counter.count++;
      };
    
      counter.count = 0;
    
      return counter;
    }
    
    let counter = makeCounter();
    
    counter.count = 10;
    alert( counter() ); // 10

    S valget af implementering afhnger af vores ml.

    Navngivet funktionsudtryk (Named Function Expression)

    Navngivet funktionsudtryk eller NFE er et udtryk for funktionsudtryk der har et navn.

    Tag for eksempel et almindeligt funktionsudtryk:

    let sayHi = function(who) {
      alert(`Hej, ${who}`);
    };

    og tilfj et navn til det:

    let sayHi = function func(who) {
      alert(`Hej, ${who}`);
    };

    Men hvad opnr vi ved det? Hvad er meningen med et ekstra func som navn?

    Det frste vi skal huske er, at vi stadig har et funktionsudtryk. Tilfjelsen af navnet "func" efter function gjorde det ikke til en funktionsdeklaration, fordi det stadig er oprettet som en del af et tildelingsudtryk.

    Tilfjelsen af et navn pvirker ikke noget andet.

    Funktionen er stadig tilgngelig som sayHi():

    let sayHi = function func(who) {
      alert(`Hej, ${who}`);
    };
    
    sayHi("John"); // Hej, John

    Der er to srlige ting ved navnet func som kan udnyttes:

    1. Det tillader funktionen at referere til sig selv internt.
    2. Det er ikke synligt uden for funktionen.

    For eksempel, funktionen sayHi nedenfor kalder sig selv igen med "Guest" hvis ingen who er angivet:

    let sayHi = function func(who) {
      if (who) {
        alert(`Hej, ${who}`);
      } else {
        func("Guest"); // brug func til at kalde dig selv igen
      }
    };
    
    sayHi(); // Hej, Guest
    
    // Men dette virker ikke:
    func(); // Fejl, func er ikke defineret (ikke synlig uden for funktionen)

    Men hvorfor vil vi bruge func? Hvorfor ikke bare bruge sayHi for det indlejrede kald?

    Vi kan faktisk i de fleste tilflde bruge sayHi for det indlejrede kald, og det vil fungere fint:

    let sayHi = function(who) {
      if (who) {
        alert(`Hej, ${who}`);
      } else {
        sayHi("Guest");
      }
    };

    Problemet med denne kode er at sayHi kan ndres i det ydre kode. Hvis funktionen bliver tildelt til en anden variabel i stedet, vil koden begynde at give fejl:

    let sayHi = function(who) {
      if (who) {
        alert(`Hej, ${who}`);
      } else {
        sayHi("Guest"); // Fejl: sayHi er ikke en funktion
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Fejl, det indlejrede sayHi kald virker ikke mere!

    Dette sker fordi funktionen tager sayHi fra dens ydre leksikale milj. Der er ingen lokal sayHi, s den ydre variabel bruges. Og ved det jeblik hvor funktionen kaldes, er den ydre sayHi null.

    Det valgfrie navn som vi kan tilfje til et funktionsudtryk er ment til at lse prcis disse typer af problemer.

    Lad os bruge det til at fikse vores kode:

    let sayHi = function func(who) {
      if (who) {
        alert(`Hej, ${who}`);
      } else {
        func("Guest"); // Nu virker det
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Hej, Guest (indlejret kald virker)

    Nu virker det fordi navnet "func" er funktionslokal. Det er ikke taget fra uden for (og ikke synligt der). Speficikationen garanterer at det altid vil referere til den nuvrende funktion.

    Det ydre kode har stadig sin variabel sayHi eller welcome. Og func er et internt funktionsnavn, mden for funktionen at kalde sig selv p p en plidelig mde.

    Du har ikke den mulighed med en funktionserklring

    Det interne navn er kun tilgngeligt for funktionsudtryk, ikke for funktionserklringer. For funktionserklringer findes der ingen syntaks til at tilfje et internt navn.

    Hvis vi har brug for et plideligt internt navn, er det den grund til at omskrive en funktionserklring til et navngivet funktionsudtryk (Named Function Expression).

    Opsummering

    Funktioner er objekter.

    Vi har deres egenskaber.:

    • name funktionens navn. Ofte taget fra funktionsdefinitionen, men hvis der ikke er nogen, forsger JavaScript at gtte det ud fra konteksten (f.eks. en tildeling).
    • length antallet af argumenter i funktionsdefinitionen. Rest-parametre tlles ikke.

    Hvis funktionen er deklareret som et funktionsudtryk (ikke i hovedkodeflowet), og den brer et navn, s kaldes det et navngivet funktionsudtryk (Named Function Expression). Navnet kan bruges inde i funktionen til at referere til sig selv, for rekursive kald eller lignende.

    Desuden kan funktioner ogs bre yderligere egenskaber. Mange kendte JavaScript-biblioteker gr brug af denne funktion.

    De opretter en hoved funktion og tilkobler mange hjlpe funktioner til den. For eksempel opretter biblioteket jQuery en funktion kaldet $. Biblioteket lodash opretter en funktion kaldet _ og tilfjer _.clone, _.keyBy og andre egenskaber til det (se eventuelt dokumentationen hvis du vil lre mere om dem). Faktisk gr de det for at mindske deres forurening af det globale objekt, s en enkelt bibliotek kun giver n global variabel. Det reducerer muligheden for navnekonflikter.

    S en funktion kan udfre en nyttig opgave alene men ogs udvide dens muligheder ved at udnytte dens egne egenskaber.

    Opgaver

    vigtighed: 5

    Juster p koden makeCounter() s tlleren ogs kan formindske tallet og stte den til et bestemt tal:

    • counter() skal returnere det nste tal (as before).
    • counter.set(value) skal stte tlleren til value.
    • counter.decrease() skal formindske tlleren med 1.

    Se sandbox-koden for det fulde brugseksempel.

    P.S. Du kan bruge enten en closure eller funktionsegenskab til at gemme den nuvrende tller. Eller skriv begge varianter.

    bn en sandbox med tests.

    lsning

    Lsningen bruger count i den lokale variabel, men tilfjede metoder er skrevet direkte ind i counter. De deler det samme ydre leksikale milj og kan ogs tilg den nuvrende count.

    function makeCounter() {
      let count = 0;
    
      function counter() {
        return count++;
      }
    
      counter.set = value => count = value;
    
      counter.decrease = () => count--;
    
      return counter;
    }

    bn lsningen med tests i en sandbox.

    vigtighed: 2

    Skriv funktionen sum som virker som dette:

    sum(1)(2) == 3; // 1 + 2
    sum(1)(2)(3) == 6; // 1 + 2 + 3
    sum(5)(-1)(2) == 6
    sum(6)(-1)(-2)(-3) == 0
    sum(0)(1)(2)(3)(4)(5) == 15

    Hint: du vil vre ndt til at oprette en brugerdefineret objekt til primitiv konvertering for for at de virker.

    bn en sandbox med tests.

    lsning
    1. For at det overhovedet komme til at virke, skal resultatet af sum vre en funktion.
    2. Den funktion skal gemme den nuvrende vrdi mellem kald.
    3. Iflge opgaven skal funktionen blive til et tal nr den bruges i ==. Funktioner er objekter, s konverteringen sker som beskrevet i kapitlet Konvertering fra objekt til primitiv, og vi kan give en egen metode der returnerer tallet.

    Her er koden:

    function sum(a) {
    
      let currentSum = a;
    
      function f(b) {
        currentSum += b;
        return f;
      }
    
      f.toString = function() {
        return currentSum;
      };
    
      return f;
    }
    
    alert( sum(1)(2) ); // 3
    alert( sum(5)(-1)(2) ); // 6
    alert( sum(6)(-1)(-2)(-3) ); // 0
    alert( sum(0)(1)(2)(3)(4)(5) ); // 15

    Bemrk at funktionen sum virker kun n gang. Den returnerer funktionen f.

    Herefter, p hvert efterflgende kald, tilfjer f sin parameter til summen currentSum, og returnerer sig selv.

    Der er ingen rekursion i den sidste linje af f.

    Her er hvad rekursion ser ud som:

    function f(b) {
      currentSum += b;
      return f(); // <-- rekursivt kald
    }

    Og i vores tilflde returnerer vi bare funktionen, uden at kalde den:

    function f(b) {
      currentSum += b;
      return f; // <-- den kalder ikke sig selv. Den returnerer sig selv
    }

    Denne f vil blive brugt i det nste kald og igen returnere sig selv, s mange gange som ndvendigt. Herefter, nr den bruges som et tal eller en streng returnerer toString currentSum. Vi kunne ogs bruge Symbol.toPrimitive eller valueOf her til konverteringen.

    bn lsningen med tests i en sandbox.

    Tutorial-oversigt

    Kommentarer

    ls dette fr du kommenterer
    • Hvis du har forslag til forbedringer - s opret venligst et GitHub-issue eller en pull request i stedet for at kommentere.
    • Hvis du ikke forstr noget i artiklen - s uddyb venligst.
    • For at indstte f ord kode, brug <code>-taggen, for flere linjer - omslut dem i <pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page