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

Oggetto funzione, NFE
IT

Vorremo rendere disponibile questo progetto open-source per persone in tutto il mondo.

Aiutaci a tradurre il contenuto di questo tutorial nella tua lingua!

    Cerca in Javascript.info:
    Cerca nel tutorial:
    Light themeDark theme
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Come gi sappiamo, in JavaScript le funzioni sono valori.

    Inoltre ogni valore ha un tipo. Di che tipo una funzione?

    In JavaScript, le funzioni sono oggetti.

    Un ottimo modo per pensare alle funzioni quello di immaginarle come oggetti attivi (che compiono azioni). Oltre a invocarli, possiamo trattarli come veri e propri oggetti: aggiungendo/rimuovendo propriet, passarli per riferimento.

    La propriet name

    Gli oggetti funzione contengono alcune propriet utili.

    Ad esempio, il nome di una funzione accessible tramite la propriet name:

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

    Inoltre lassegnazione della propriet name intelligente. Funziona anche se dichiariamo la funzione per assegnazione ad una variabile:

    let sayHi = function() {
      alert("Hi");
    };
    
    alert(sayHi.name); // sayHi (funziona!)

    Funziona anche nel caso in cui lassegnazione viene effettuata come valore di default:

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

    Nelle specifiche, questa caratteristica viene chiamata contextual name (nome prelevato dal contesto). Se la funzione non ne fornisce uno, allora durante lassegnazione questo viene ricavato dal contesto.

    Anche i metodi delloggetto possiedono la propriet name:

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

    Non sta accadendo nulla di magico. Anche se ci sono dei casi in cui non c alcun modo di ricavare il nome dal contesto. In questi casi, la propriet name sar vuota, come nellesempio:

    // funzione creata all'interno dell'array
    let arr = [function() {}];
    
    alert( arr[0].name ); // <stringa vuota>
    // il motore non ha alcun modo di ricavare il nome corretto, per questo sar vuoto

    Nella pratica per, la maggior parte delle funzioni possiedono un nome.

    La propriet length

    Esiste unaltra propriet molto utile, length che ritorna il numero di parametri della funzione, ad esempio:

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

    Da questo esempio possiamo notare che i parametri di resto non vengono contati.

    La propriet length viene spesso utilizzata per ispezionare un funzione che opera su altre funzioni.

    Ad esempio, nel codice sotto la funzione ask accetta una question da porre, e un numero arbitrario di handler (gestori) da invocare.

    Una volta che lutente ha fornito la risposta, la funzione invoca gli handlers. Possiamo fornire due tipi di handlers:

    • Una funzione con zero argomenti, che viene in invocata solamente nel caso in cui lutente fornisca una risposta positiva.
    • Una funzione con argomenti, che viene invocata in entrambi i casi e ritorna una risposta.

    Lidea quella di avere un semplice handles senza argomenti, per gestire i casi positivi (la variante pi frequente), ma siamo comunque in grado di fornire un gestore universale.

    Per invocare handlers nel modo corretto, esaminiamo la propriet length:

    function ask(question, ...handlers) {
      let isYes = confirm(question);
    
      for(let handler of handlers) {
        if (handler.length == 0) {
          if (isYes) handler();
        } else {
          handler(isYes);
        }
      }
    
    }
    
    // per risposte positive, entrambi gli handler vengono chiamati
    // per le risposte negative, solamente la seconda
    ask("Question?", () => alert('You said yes'), result => alert(result));

    Questo un caso particolare di quello che viene chiamato polimorfismo trattare gli argomenti in maniera differente in base al loro tipo, o nel nostro caso in base a length. Questidea ha delle utili applicazioni nelle librerie JavaScript.

    Propriet aggiuntive

    Possiamo anche aggiungere delle propriet.

    In questo esempio aggiungiamo la propriet counter per tenere traccia delle chiamate totali:

    function sayHi() {
      alert("Hi");
    
      // andiamo a contare quante volte verr invocata
      sayHi.counter++;
    }
    sayHi.counter = 0; // valore iniziale
    
    sayHi(); // Hi
    sayHi(); // Hi
    
    alert( `Called ${sayHi.counter} times` ); // Chiamata 2 volte
    Una propriet non una variabile

    Una propriet assegnata ad una funzione, come sayHi.counter = 0 non definisce una variabile locale counter. In altre parole, una propriet counter ed una variabile let counter sono due cose separate.

    Possiamo quindi trattare una funzione come un oggetto, memorizzare propriet, ma non avranno alcun effetto sullesecuzione. Le variabili non utilizzano mai le propriet della funzione e vice versa. Sono come due mondi paralleli.

    Le propriet delle funzioni possono rimpiazzare le closure in alcun casi. Ad esempio, possiamo riscrivere la funzione contatore del capitolo Variable scope, closure sfruttando una propriet della funzione:

    function makeCounter() {
      // piuttosto di:
      // let count = 0
    
      function counter() {
        return counter.count++;
      };
    
      counter.count = 0;
    
      return counter;
    }
    
    let counter = makeCounter();
    alert( counter() ); // 0
    alert( counter() ); // 1

    Ora count viene memorizzato direttamente nella funzione, non nel Lexical Environment esterno.

    Questa soluzione migliori o peggiore rispetto ad una closure?

    La principale differenza che se il valore di count sta su una variabile esterna, allora il codice al suo esterno non vi pu accedere. Solamente le funzioni annidate possono modificarla. Se invece questa legata ad una funzione, possono accadere cose simili:

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

    Quindi non c alcuna scelta migliore, ogni caso va analizzato.

    Named Function Expression

    Named Function Expression (Espressione di Funzione con Nome), o NFE, un termine per riferirsi alle espressioni di funzioni che hanno un nome.

    Ad esempio, prendiamo una normale espressione di funzione:

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

    E diamogli un nome:

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

    Abbiamo ottenuto qualcosa? Qual lo scopo di aggiungere il nome "func"?

    Innanzitutto vale la pena notare che continua ad essere un espressione di funzione. Aggiungere il nome "func" dopo function non la rende una dichiarazione di funzione, perch viene comunque creata come una parte di un assegnazione.

    Quindi aggiungere un nome non provoca assolutamente nessuno danno.

    La funzione rimane comunque disponibile come sayHi():

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

    Ci sono due cose che rendono speciale il nome func:

    1. Consente alla funzione di riferirsi a se stessa internamente.
    2. Non visibile allesterno della funzione.

    Ad esempio, la funzione sayHi qui sotto, chiama nuovamente se stessa con "Guest" se non viene fornito alcun who:

    let sayHi = function func(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        func("Guest"); // utilizza func per richiamare se stessa
      }
    };
    
    sayHi(); // Hello, Guest
    
    // Questo non funziona:
    func(); // Errore, func non  definita (non  visibile all'esterno)

    Perch utilizziamo func? Forse potremmo semplicemente chiamare sayHi per le chiamate annidate?

    In realt, in molti possibile:

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

    Il problema con questo codice che il valore di sayHi potrebbe cambiare. La funzione potrebbe essere trasferita su unaltra variabile, e il codice diventerebbe errato:

    let sayHi = function(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        sayHi("Guest"); // Errore: sayHi non  una funzione
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Errore, la chiamata annidata a sayHi non  pi in funzione!

    Questo accade perch la funzione prende sayHi dal suo lexical environment esterno. Non c alcun sayHi locale, quindi viene utilizzata la variabile esterna. Nellesempio sopra al momento della chiamata il valore di sayHi null.

    La possibilit di aggiungere un nome ad un espressione di funzione pensato proprio per risolvere questo tipo di problemi.

    Sfruttiamo questa caratteristica per sistemare il codice:

    let sayHi = function func(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        func("Guest"); // Ora  tutto okay
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Hello, Guest (la chiamata annidata funziona correttamente)

    Ora funzione, perch il nome "func" locale alla funzione. Non viene prelevato dallesterno. Le specifiche garantisco che in questo modo si avr sempre un riferimento alla funzione corrente.

    Il codice esterno continuer ad utilizzare la variabile sayHi o welcome. E func servir da nome interno della funzione.

    Tutto questo non vale per la dichiarazione di funzione

    La caratteristica del nome interno disponibile solamente per le espressioni di funzione, non per le dichiarazioni di funzione. Per le dichiarazioni di funzione, non c alcun modo per aggiungere un ulteriore nome interno.

    Talvolta, nel caso in cui avessimo bisogno di un nome interno, potrebbe essere sensato riscrivere la dichiarazione di funzione come espressione di funzione.

    Riepilogo

    Le funzioni sono oggetti.

    Qui abbiamo visto le propriet:

    • name il nome della funzione. Non esiste solamente quando viene fornito nella definizione della funzione, ma anche nel caso di assegnazioni o propriet di un oggetto.
    • length il numero di argomenti nella definizione della funzione. I parametri di resto non vengono contati.

    Se una funzione viene dichiarata come espressione di funzione (non nel principale flusso di codice), e possiede un nome, questa viene definita una Named Function Expression. Il nome pu essere utilizzato internamente per auto-riferimenti, per chiamate ricorsive e altri contesti simili.

    Inoltre, una funzione pu possedere diverse propriet aggiuntive. Molte librerie JavaScript fanno largo utilizzo di questa caratteristica.

    Queste creano una funzione principale e ci attaccano molte altre funzioni di supporto. Ad esempio la libreria jquery definisce una funzione chiamata $. La libreria lodash definisce una funzione _. E ci aggiunge _.clone, _.keyBy e altre propriet (vedi la documentazione. In realt, lo fanno anche per diminuire la sporcizia nello spazio globale, in questo modo una libreria fornisce una sola variabile globale. Questo riduce la probabilit di conflitti tra nomi.

    Quindi una funzione, oltre ad essere utile, pu fornire un insieme di altre funzionalit grazie alle propriet.

    Esercizi

    importanza: 5

    Modificate il codice di makeCounter() in modo tale che il counter possa essere anche decrementato e reimpostato:

    • counter() dovrebbe ritornare il prossimo numero (come gi fa).
    • counter.set(value) dovrebbe impostare il contatore a value.
    • counter.decrease() dovrebbe decremetare il contatore di 1.

    Vedi il codice in sandbox per un esempio completo.

    P.S. Potete usare sia una closure che una propriet di funzione. O scrivere entrambe le varianti.

    Apri una sandbox con i test.

    soluzione

    La soluzione utilizza count come variabile locale, ma i metodi aggiuntivi sono scritti dentro counter. Condividono lo stesso lexical environment esterno e possono accedere al valore di count.

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

    Apri la soluzione con i test in una sandbox.

    importanza: 2

    Scrivete una funzione sum che funzioni in questo modo:

    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

    P.S. Aiuto: potresti impostare una conversione toPrimitive del tuo oggetto.

    Apri una sandbox con i test.

    soluzione
    1. Per far si che che funzioni comunque, il risultato di sum deve essere una funzione.
    2. Questa funzione deve tenere in memoria il valore corrente.
    3. Come richiesto dallesercizio, la funzione deve essere convertita in numero quando viene utilizzata con ==. Le funzioni sono oggetti, quindi la conversione avviene come descritto nel capitolo Conversione da oggetto a primitivi, e possiamo fornire un nostro metodo che si occupi di trasformare la funzione in tipo numerico.

    Il codice:

    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

    Da notare che la funzione sum esegue una sola volta. Ritorna una funzione f.

    Poi, in ogni chiamata successiva, f aggiunge il suo parametro alla somma presente in currentSum, e ritorna se stessa.

    Non c ricorsione nellultima linea di f.

    Una ricorsione appare in questo modo:

    function f(b) {
      currentSum += b;
      return f(); // <-- chiamata ricorsiva
    }

    Nel nostro caso, semplicemente ritorniamo una funzione, senza effettuare alcuna chiamata:

    function f(b) {
      currentSum += b;
      return f; // <-- non viene invocata, ritorna solamente se stessa
    }

    Questa f verr utilizzata nella chiamata successiva, e ritorner ancora se stessa, tutte le volte che sar necessario. Quindi, quando la utilizzeremo come numero o stringa, toString ritorna la currentSum. Possiamo anche utilizzare Symbol.toPrimitive o valueOf per la conversione.

    Apri la soluzione con i test in una sandbox.

    Mappa del tutorial

    Commenti

    leggi questo prima di lasciare un commento
    • Per qualsiasi suggerimento - per favore, apri una issue su GitHub o una pull request, piuttosto di lasciare un commento.
    • Se non riesci a comprendere quanto scitto nell'articolo ti preghiamo di fornire una spiegazione chiara.
    • Per inserire delle righe di codice utilizza il tag <code>, per molte righe includile nel tag <pre>, per pi di 10 righe utilizza una sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page