Ricerca…


introduzione

Le funzioni in JavaScript forniscono un codice organizzato e riutilizzabile per eseguire una serie di azioni. Le funzioni semplificano il processo di codifica, impediscono la logica ridondante e rendono il codice più facile da seguire. Questo argomento descrive la dichiarazione e l'utilizzo di funzioni, argomenti, parametri, dichiarazioni di ritorno e scope in JavaScript.

Sintassi

  • esempio di funzione (x) {return x}

  • var esempio = function (x) {return x}

  • (funzione() { ... })(); // Espressione funzione Invocato immediatamente (IIFE)

  • var instance = new Esempio (x);

  • metodi

  • fn.apply (valueForThis [, arrayOfArgs])

  • fn.bind (valueForThis [, arg1 [, arg2, ...]])

  • fn.call (valueForThis [, arg1 [, arg2, ...]])

  • ES2015 + (ES6 +):

  • const example = x => {return x}; // Ritorno esplicito della funzione freccia

  • const esempio = x => x; // Ritorno implicito della funzione Arrow

  • const example = (x, y, z) => {...} // Arrow function argomenti multipli

  • (() => {...}) (); // IIFE utilizzando una funzione freccia

Osservazioni

Per informazioni sulle funzioni delle frecce, vedere la documentazione delle funzioni Arrow .

Funziona come una variabile

Una dichiarazione di funzione normale si presenta così:

function foo(){
}

Una funzione definita come questa è accessibile da qualsiasi parte all'interno del suo contesto dal suo nome. Ma a volte può essere utile trattare i riferimenti alle funzioni come i riferimenti agli oggetti. Ad esempio, è possibile assegnare un oggetto a una variabile in base ad alcune serie di condizioni e successivamente recuperare una proprietà dall'uno o dall'altro oggetto:

var name = 'Cameron';
var spouse;

if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };

var spouseName = spouse.name;

In JavaScript, puoi fare la stessa cosa con le funzioni:

// Example 1
var hashAlgorithm = 'sha1';
var hash;

if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };

hash('Fred');

Nell'esempio sopra, l' hash è una variabile normale. Viene assegnato un riferimento a una funzione, dopodiché la funzione a cui fa riferimento può essere invocata usando parentesi, proprio come una normale dichiarazione di funzione.

L'esempio sopra fa riferimento alle funzioni anonime ... funzioni che non hanno il loro nome. È anche possibile utilizzare le variabili per fare riferimento a funzioni con nome. L'esempio sopra potrebbe essere riscritto in questo modo:

// Example 2
var hashAlgorithm = 'sha1';
var hash;

if ( hashAlgorithm === 'sha1' ) hash = sha1Hash;
else if ( hashAlgorithm === 'md5' ) hash = md5Hash;

hash('Fred');

function md5Hash(value){
    // ...
}

function sha1Hash(value){
    // ...
}

Oppure, puoi assegnare i riferimenti alle funzioni dalle proprietà dell'oggetto:

// Example 3
var hashAlgorithms = {
    sha1: function(value) { /**/ },
    md5: function(value) { /**/ }
};

var hashAlgorithm = 'sha1';
var hash;

if ( hashAlgorithm === 'sha1' ) hash = hashAlgorithms.sha1;
else if ( hashAlgorithm === 'md5' ) hash = hashAlgorithms.md5;

hash('Fred');

È possibile assegnare il riferimento a una funzione tenuta da una variabile a un'altra omettendo le parentesi. Ciò può comportare un errore facile da fare: tentare di assegnare il valore di ritorno di una funzione a un'altra variabile, ma assegnare casualmente il riferimento alla funzione.

// Example 4
var a = getValue;
var b = a; // b is now a reference to getValue.
var c = b(); // b is invoked, so c now holds the value returned by getValue (41)

function getValue(){
    return 41;
}

Un riferimento a una funzione è come qualsiasi altro valore. Come hai visto, un riferimento può essere assegnato a una variabile e il valore di riferimento di tale variabile può essere successivamente assegnato ad altre variabili. È possibile passare riferimenti a funzioni come qualsiasi altro valore, incluso il passaggio di un riferimento a una funzione come valore di ritorno di un'altra funzione. Per esempio:

// Example 5
// getHashingFunction returns a function, which is assigned
// to hash for later use:
var hash = getHashingFunction( 'sha1' );
// ...
hash('Fred');


// return the function corresponding to the given algorithmName
function getHashingFunction( algorithmName ){
    // return a reference to an anonymous function
    if (algorithmName === 'sha1') return function(value){ /**/ };
    // return a reference to a declared function
    else if (algorithmName === 'md5') return md5;
}

function md5Hash(value){
    // ...
}

Non è necessario assegnare un riferimento di funzione a una variabile per poterlo richiamare. Questo esempio, compilando l'esempio 5, chiamerà getHashingFunction e quindi richiamerà immediatamente la funzione restituita e passerà il suo valore di ritorno a hashedValue.

// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );

Una nota sul sollevamento

Tieni presente che, a differenza delle normali dichiarazioni di funzioni, le variabili che fanno riferimento alle funzioni non vengono "issate". Nell'esempio 2, le funzioni md5Hash e sha1Hash sono definite nella parte inferiore dello script, ma sono immediatamente disponibili ovunque. Indipendentemente da dove si definisce una funzione, l'interprete lo "issa" in cima alla sua portata, rendendolo immediatamente disponibile. Questo non è il caso delle definizioni di variabili, quindi il codice come il seguente si interromperà:

var functionVariable;

hoistedFunction(); // works, because the function is "hoisted" to the top of its scope
functionVariable(); // error: undefined is not a function.

function hoistedFunction(){}
functionVariable = function(){};

Funzione anonima

Definizione di una funzione anonima

Quando una funzione è definita, spesso le dai un nome e poi la invocano usando quel nome, in questo modo:

foo();

function foo(){
    // ...
}

Quando si definisce una funzione in questo modo, il runtime Javascript memorizza la funzione in memoria e quindi crea un riferimento a tale funzione, utilizzando il nome che gli è stato assegnato. Questo nome è quindi accessibile nell'ambito corrente. Questo può essere un modo molto conveniente per creare una funzione, ma Javascript non richiede di assegnare un nome a una funzione. Quanto segue è anche perfettamente legale:

function() {
    // ...
}

Quando una funzione è definita senza un nome, è conosciuta come una funzione anonima. La funzione è archiviata in memoria, ma il runtime non crea automaticamente un riferimento ad esso per te. A prima vista, potrebbe sembrare che una cosa del genere non avrebbe alcun senso, ma ci sono diversi scenari in cui le funzioni anonime sono molto convenienti.

Assegnazione di una funzione anonima a una variabile

Un uso molto comune delle funzioni anonime è assegnarle a una variabile:

var foo = function(){ /*...*/ };

foo();

Questo uso di funzioni anonime è trattato più dettagliatamente in Funzioni come variabile

Fornire una funzione anonima come parametro ad un'altra funzione

Alcune funzioni possono accettare un riferimento a una funzione come parametro. Questi sono a volte indicati come "iniezioni di dipendenza" o "callback", perché consentono alla funzione che la chiamata chiama di "richiamare" il codice, offrendoti l'opportunità di cambiare il modo in cui si comporta la funzione chiamata. Ad esempio, la funzione mappa dell'oggetto Array consente di eseguire iterazioni su ciascun elemento di una matrice, quindi creare un nuovo array applicando una funzione di trasformazione a ciascun elemento.

var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]

Sarebbe noioso, sciatto e inutile creare una funzione con nome, che ingombrerebbe il tuo obiettivo con una funzione necessaria solo in questo luogo e interromperà il flusso naturale e la lettura del tuo codice (un collega dovrebbe lasciare questo codice per trovare il tuo funzione per capire cosa sta succedendo).

Restituzione di una funzione anonima da un'altra funzione

A volte è utile restituire una funzione come risultato di un'altra funzione. Per esempio:

var hash = getHashFunction( 'sha1' );
var hashValue = hash( 'Secret Value' );

function getHashFunction( algorithm ){

    if ( algorithm === 'sha1' ) return function( value ){ /*...*/ };
    else if ( algorithm === 'md5' ) return function( value ){ /*...*/ };

}

Richiamare immediatamente una funzione anonima

A differenza di molti altri linguaggi, lo scope in Javascript è a livello di funzione, non a livello di blocco. (Vedi Funzione Scoping ). In alcuni casi, tuttavia, è necessario creare un nuovo ambito. Ad esempio, è comune creare un nuovo ambito quando si aggiunge codice tramite un tag <script> , piuttosto che consentire la definizione di nomi di variabili nell'ambito globale (che rischia di far scontrare altri script con i nomi delle variabili). Un metodo comune per gestire questa situazione è definire una nuova funzione anonima e quindi invocarla immediatamente, nascondendo le variabili in modo sicuro nell'ambito della funzione anonima e senza rendere il proprio codice accessibile a terze parti tramite un nome di funzione trapelato. Per esempio:

<!-- My Script -->
<script>
function initialize(){
    // foo is safely hidden within initialize, but...
    var foo = '';
}

// ...my initialize function is now accessible from global scope.
// There's a risk someone could call it again, probably by accident.
initialize();
</script>

<script>
// Using an anonymous function, and then immediately
// invoking it, hides my foo variable and guarantees
// no one else can call it a second time.
(function(){
    var foo = '';
}()) // <--- the parentheses invokes the function immediately
</script>

Funzioni anonime autoreferenti

A volte è utile che una funzione anonima sia in grado di riferirsi a se stessa. Ad esempio, potrebbe essere necessario che la funzione si richiami in modo ricorsivo o aggiunga proprietà a se stesso. Se la funzione è anonima, tuttavia, può essere molto difficile in quanto richiede la conoscenza della variabile a cui è stata assegnata la funzione. Questa è la soluzione meno che ideale:

var foo = function(callAgain){
    console.log( 'Whassup?' );
    // Less then ideal... we're dependent on a variable reference...
    if (callAgain === true) foo(false);
};

foo(true);

// Console Output:
// Whassup?
// Whassup?

// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
    console.log('Bad.')
};

bar(true);

// Console Output:
// Whassup?
// Bad.

L'intento qui era che la funzione anonima chiamasse ricorsivamente se stessa, ma quando il valore di foo cambia, si finisce con un bug potenzialmente difficile da rintracciare.

Invece, possiamo dare alla funzione anonima un riferimento a se stessa dandogli un nome privato, in questo modo:

var foo = function myself(callAgain){
    console.log( 'Whassup?' );
    // Less then ideal... we're dependent on a variable reference...
    if (callAgain === true) myself(false);
};

foo(true);

// Console Output:
// Whassup?
// Whassup?

// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
    console.log('Bad.')
};

bar(true);

// Console Output:
// Whassup?
// Whassup?

Si noti che il nome della funzione è limitato a se stesso. Il nome non è trapelato nello scope esterno:

myself(false); // ReferenceError: myself is not defined

Questa tecnica è particolarmente utile quando si gestiscono le funzioni anonimi ricorsive come parametri di callback:

5
// Calculate the fibonacci value for each number in an array:
var fib = false,
    result = [1,2,3,4,5,6,7,8].map(
        function fib(n){
            return ( n <= 2 ) ? 1 : fib( n - 1 ) + fib( n - 2 );
        });
// result = [1, 1, 2, 3, 5, 8, 13, 21]
// fib = false (the anonymous function name did not overwrite our fib variable)

Espressioni di funzioni invocate immediatamente

A volte non vuoi avere la tua funzione accessibile / memorizzata come variabile. È possibile creare un'espressione funzione immediatamente richiamata (IIFE in breve). Queste sono essenzialmente funzioni anonime autoeseguite . Hanno accesso all'ambito circostante, ma la funzione stessa e qualsiasi variabile interna saranno inaccessibili dall'esterno. Una cosa importante da notare su IIFE è che anche se si nomina la propria funzione, IIFE non viene issato come le funzioni standard e non possono essere chiamate dal nome della funzione con cui sono dichiarate.

(function() {
   alert("I've run - but can't be run again because I'm immediately invoked at runtime,
          leaving behind only the result I generate");
}());

Questo è un altro modo per scrivere IIFE. Si noti che la parentesi di chiusura prima del punto e virgola è stata spostata e posizionata subito dopo la parentesi graffa di chiusura:

(function() {
   alert("This is IIFE too.");
})();

Puoi facilmente passare i parametri in un IIFE:

(function(message) {
   alert(message);
}("Hello World!"));

Inoltre, è possibile restituire valori all'ambito circostante:

var example = (function() {
   return 42;
}());
console.log(example); // => 42

Se necessario è possibile nominare un IIFE. Sebbene sia visto meno spesso, questo pattern presenta diversi vantaggi, come fornire un riferimento che può essere usato per una ricorsione e può semplificare il debugging come il nome è incluso nel callstack.

(function namedIIFE() { 
    throw error; // We can now see the error thrown in 'namedIIFE()'
}());

Mentre avvolgere una funzione tra parentesi è il modo più comune per indicare al parser Javascript di aspettarsi un'espressione, nei luoghi in cui è già prevista un'espressione, la notazione può essere resa più concisa:

var a = function() { return 42 }();
console.log(a)  // => 42

Versione della freccia della funzione immediatamente richiamata:

6
(() => console.log("Hello!"))(); // => Hello!

Funzione Scoping

Quando si definisce una funzione, viene creato un ambito .

Tutto ciò che è definito all'interno della funzione non è accessibile dal codice al di fuori della funzione. Solo il codice all'interno di questo ambito può vedere le entità definite all'interno dell'ambito.

function foo() {
  var a = 'hello';
  console.log(a);  // => 'hello'
}

console.log(a);  // reference error

Le funzioni annidate sono possibili in JavaScript e si applicano le stesse regole.

function foo() {
  var a = 'hello';
  
  function bar() {
    var b = 'world';
    console.log(a);  // => 'hello'
    console.log(b);  // => 'world'
  }

  console.log(a);  // => 'hello'
  console.log(b);  // reference error
}

console.log(a);  // reference error
console.log(b);  // reference error

Quando JavaScript tenta di risolvere un riferimento o una variabile, inizia a cercarlo nell'ambito corrente. Se non riesce a trovare quella dichiarazione nello scope corrente, si arrampica su un ambito per cercarlo. Questo processo si ripete finché non viene trovata la dichiarazione. Se il parser JavaScript raggiunge l'ambito globale e ancora non riesce a trovare il riferimento, verrà generato un errore di riferimento.

var a = 'hello';

function foo() {
  var b = 'world';

  function bar() {
    var c = '!!';

    console.log(a);  // => 'hello'
    console.log(b);  // => 'world'
    console.log(c);  // => '!!'
    console.log(d);  // reference error
  }
}

Questo comportamento di arrampicata può anche significare che un riferimento può "ombreggiare" su un riferimento con un nome simile nello scope esterno poiché viene visto per primo.

var a = 'hello';

function foo() {
  var a = 'world';

  function bar() {
    console.log(a);  // => 'world'
  }
}
6

Il modo in cui JavaScript risolve l'ambito si applica anche alla parola chiave const . Dichiarare una variabile con la parola chiave const implica che non è consentito riassegnare il valore, ma dichiararlo in una funzione creerà un nuovo ambito e con esso una nuova variabile.

function foo() {
  const a = true;

  function bar() {
    const a = false;  // different variable
    console.log(a);   // false
  }

  const a = false;    // SyntaxError
  a = false;          // TypeError
  console.log(a);     // true
}

Tuttavia, le funzioni non sono gli unici blocchi che creano un ambito (se si utilizza let o const ). let e le dichiarazioni const hanno un ambito dell'istruzione di blocco più vicina. Vedi qui per una descrizione più dettagliata.

Binding `this` e argomenti

5.1

Quando si fa riferimento a un metodo (una proprietà che è una funzione) in JavaScript, di solito non ricorda l'oggetto a cui era originariamente collegato. Se il metodo ha bisogno di fare riferimento a tale oggetto come this non sarà in grado di, e chiamando probabilmente causare un crash.

È possibile utilizzare il metodo .bind() su una funzione per creare un wrapper che include il valore di this e un numero qualsiasi di argomenti principali.

var monitor = {
  threshold: 5,
  check: function(value) {
    if (value > this.threshold) {
      this.display("Value is too high!");
    }
  },
  display(message) {
    alert(message);
  }
};

monitor.check(7); // The value of `this` is implied by the method call syntax.


var badCheck = monitor.check;
badCheck(15); // The value of `this` is window object and this.threshold is undefined, so value > this.threshold is false

var check = monitor.check.bind(monitor);
check(15); // This value of `this` was explicitly bound, the function works.

var check8 = monitor.check.bind(monitor, 8);
check8(); // We also bound the argument to `8` here. It can't be re-specified.

Quando non è in modalità rigorosa, una funzione utilizza l'oggetto globale ( window nel browser) come this , a meno che la funzione non venga chiamata come metodo, associata o chiamata con la sintassi del metodo .call .

window.x = 12; 

function example() {
  return this.x;
}

console.log(example()); // 12

In modalità rigorosa this è undefined per impostazione predefinita

window.x = 12; 
    
function example() {
  "use strict";
  return this.x;
}

console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
7

Bind Operator

L' operatore doppio legame dei due punti può essere utilizzato come sintassi abbreviata per il concetto spiegato sopra:

var log = console.log.bind(console); // long version
const log = ::console.log; // short version

foo.bar.call(foo); // long version
foo::bar(); // short version

foo.bar.call(foo, arg1, arg2, arg3); // long version
foo::bar(arg1, arg2, arg3); // short version

foo.bar.apply(foo, args); // long version
foo::bar(...args); // short version

Questa sintassi ti consente di scrivere normalmente, senza preoccuparti di legare this ovunque.

Funzioni della console di collegamento alle variabili

var log = console.log.bind(console);

Uso:

log('one', '2', 3, [4], {5: 5});

Produzione:

one 2 3 [4] Object {5: 5}

Perché dovresti farlo?

Un caso d'uso può essere quando si ha un logger personalizzato e si desidera decidere su runtime quale utilizzare.

var logger = require('appLogger');

var log = logToServer ? logger.log : console.log.bind(console);

Argomenti della funzione, oggetto "argomenti", parametri di pausa e diffusione

Le funzioni possono assumere input sotto forma di variabili che possono essere utilizzate e assegnate all'interno del proprio ambito. La seguente funzione accetta due valori numerici e restituisce la loro somma:

function addition (argument1, argument2){
    return argument1 + argument2; 
}

console.log(addition(2, 3)); // -> 5

arguments oggetto

L'oggetto arguments contiene tutti i parametri della funzione che contengono un valore non predefinito . Può anche essere utilizzato anche se i parametri non sono esplicitamente dichiarati:

(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]

Sebbene durante la stampa di arguments l'output sia simile a una matrice, in realtà è un oggetto:

(function() { console.log(typeof arguments) })(); // -> object

Parametri di riposo: function (...parm) {}

In ES6, la sintassi ... quando viene utilizzata nella dichiarazione dei parametri di una funzione trasforma la variabile alla sua destra in un singolo oggetto contenente tutti i parametri rimanenti forniti dopo quelli dichiarati. Ciò consente alla funzione di essere invocata con un numero illimitato di argomenti, che diventeranno parte di questa variabile:

(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123  

Parametri di diffusione: function_name(...varb);

In ES6, la sintassi ... può essere utilizzata anche quando si richiama una funzione posizionando un oggetto / variabile alla sua destra. Ciò consente agli elementi di quell'oggetto di essere passati in quella funzione come un singolo oggetto:

let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42

Funzioni nominate

Le funzioni possono essere denominate o senza nome ( funzioni anonime ):

var namedSum = function sum (a, b) { // named
    return a + b;
}

var anonSum = function (a, b) { // anonymous
    return a + b;
}

namedSum(1, 3);
anonSum(1, 3);

4
4

Ma i loro nomi sono privati ​​per il loro scopo:

var sumTwoNumbers = function sum (a, b) {
    return a + b;
}

sum(1, 3);

Uncaught ReferenceError: sum non è definito

Le funzioni con nome differiscono dalle funzioni anonime in più scenari:

  • Quando si esegue il debug, il nome della funzione verrà visualizzato nella traccia di errore / stack
  • Le funzioni con nome vengono issate mentre le funzioni anonime no
  • Le funzioni con nome e le funzioni anonime si comportano diversamente quando si gestisce la ricorsione
  • A seconda della versione di ECMAScript, le funzioni con nome e anonime possono trattare la proprietà del name funzione in modo diverso

Le funzioni con nome sono issate

Quando si utilizza una funzione anonima, la funzione può essere chiamata solo dopo la riga di dichiarazione, mentre una funzione con nome può essere chiamata prima della dichiarazione. Tenere conto

foo();
var foo = function () { // using an anonymous function
    console.log('bar');
}

Tipo non rilevato Errore: foo non è una funzione

foo();
function foo () { // using a named function
    console.log('bar');
}

bar


Funzioni nominate in uno scenario ricorsivo

Una funzione ricorsiva può essere definita come:

var say = function (times) {
    if (times > 0) {
        console.log('Hello!');

        say(times - 1);
    }
}

//you could call 'say' directly, 
//but this way just illustrates the example
var sayHelloTimes = say;

sayHelloTimes(2);

Ciao!
Ciao!

Cosa succede se da qualche parte nel codice viene ridefinito il binding della funzione originale?

var say = function (times) {
    if (times > 0) {
        console.log('Hello!');

        say(times - 1);
    }
}

var sayHelloTimes = say;
say = "oops";

sayHelloTimes(2);

Ciao!
Tipo non rilevato Errore: dire non è una funzione

Questo può essere risolto usando una funzione con nome

// The outer variable can even have the same name as the function
// as they are contained in different scopes
var say = function say (times) {
    if (times > 0) {
        console.log('Hello!');

        // this time, 'say' doesn't use the outer variable
        // it uses the named function
        say(times - 1);
    }
}

var sayHelloTimes = say;
say = "oops";

sayHelloTimes(2);

Ciao!
Ciao!

E come bonus, la funzione con nome non può essere impostata su undefined , anche dall'interno:

var say = function say (times) {
    // this does nothing
    say = undefined;
    
    if (times > 0) {
        console.log('Hello!');

        // this time, 'say' doesn't use the outer variable
        // it's using the named function
        say(times - 1);
    }
}

var sayHelloTimes = say;
say = "oops";

sayHelloTimes(2);

Ciao!
Ciao!


La proprietà del name delle funzioni

Prima di ES6, le funzioni con nome avevano le loro proprietà del name impostate sui loro nomi di funzione e le funzioni anonime avevano le loro proprietà del name impostate sulla stringa vuota.

5
var foo = function () {}
console.log(foo.name); // outputs ''

function foo () {}
console.log(foo.name); // outputs 'foo'

Post ES6, le funzioni con nome e senza nome impostano entrambe le proprietà del name :

6
var foo = function () {}
console.log(foo.name); // outputs 'foo'

function foo () {}
console.log(foo.name); // outputs 'foo'

var foo = function bar () {}
console.log(foo.name); // outputs 'bar'

Funzione ricorsiva

Una funzione ricorsiva è semplicemente una funzione, che si chiamerebbe.

function factorial (n) {
    if (n <= 1) {
        return 1;
    }
    
    return n * factorial(n - 1);
}

La funzione sopra mostra un esempio di base su come eseguire una funzione ricorsiva per restituire un fattoriale.


Un altro esempio potrebbe essere quello di recuperare la somma di numeri pari in un array.

function countEvenNumbers (arr) {
    // Sentinel value.  Recursion stops on empty array.
    if (arr.length < 1) {
        return 0;
    }
    // The shift() method removes the first element from an array 
    // and returns that element. This method changes the length of the array.
    var value = arr.shift();

    // `value % 2 === 0` tests if the number is even or odd
    // If it's even we add one to the result of counting the remainder of 
    // the array.  If it's odd, we add zero to it.
    return ((value % 2 === 0) ? 1 : 0) + countEvens(arr);
}

È importante che tali funzioni eseguano una sorta di controllo del valore sentinella per evitare loop infiniti. Nel primo esempio precedente, quando n è minore o uguale a 1, la ricorsione si interrompe, consentendo di restituire il risultato di ogni chiamata sullo stack delle chiamate.

accattivarsi

Il Currying è la trasformazione di una funzione di n arità o argomenti in una sequenza di n funzioni che richiedono un solo argomento.

Casi d'uso: quando i valori di alcuni argomenti sono disponibili prima degli altri, è possibile utilizzare il currying per scomporre una funzione in una serie di funzioni che completano il lavoro in fasi successive all'arrivo di ogni valore. Questo può essere utile:

  • Quando il valore di un argomento non cambia quasi mai (ad esempio, un fattore di conversione), ma è necessario mantenere la flessibilità dell'impostazione di quel valore (piuttosto che codificarlo come costante).
  • Quando il risultato di una funzione al curry è utile prima dell'esecuzione delle altre funzioni elaborate.
  • Convalidare l'arrivo delle funzioni in una sequenza specifica.

Ad esempio, il volume di un prisma rettangolare può essere spiegato da una funzione di tre fattori: lunghezza ( l ), larghezza ( w ) e altezza ( h ):

var prism = function(l, w, h) {
    return l * w * h;
}

Una versione al curry di questa funzione sarà simile a:

function prism(l) {
    return function(w) {
        return function(h) {
            return l * w * h;
        }
    }
}
6
// alternatively, with concise ECMAScript 6+ syntax:
var prism = l => w => h => l * w * h;

Puoi chiamare queste sequenze di funzioni con prism(2)(3)(5) , che dovrebbe valutare a 30.

Senza alcuni macchinari aggiuntivi (come con le librerie), il curring è di limitata flessibilità sintattica in JavaScript (ES 5/6) a causa della mancanza di valori di segnaposto; quindi, mentre puoi usare var a = prism(2)(3) per creare una funzione parzialmente applicata , non puoi usare prism()(3)(5) .

Utilizzando la dichiarazione di reso

L'istruzione return può essere un modo utile per creare output per una funzione. L'istruzione return è particolarmente utile se non si sa in che contesto verrà utilizzata la funzione.

//An example function that will take a string as input and return 
//the first character of the string.

function firstChar (stringIn){
    return stringIn.charAt(0);
}

Ora per usare questa funzione, devi metterla al posto di una variabile da qualche altra parte nel tuo codice:

Usando il risultato della funzione come argomento per un'altra funzione:

console.log(firstChar("Hello world"));

L'output della console sarà:

> H

L'istruzione return termina la funzione

Se modifichiamo la funzione all'inizio, possiamo dimostrare che l'istruzione return termina la funzione.

function firstChar (stringIn){
    console.log("The first action of the first char function");
    return stringIn.charAt(0);
    console.log("The last action of the first char function");
}

L'esecuzione di questa funzione in questo modo sarà simile a questa:

console.log(firstChar("JS"));

Uscita della console:

> The first action of the first char function
> J

Non stamperà il messaggio dopo l'istruzione return, poiché la funzione è stata terminata.

Istruzione di ritorno che si estende su più righe:

In JavaScript, puoi normalmente dividere una linea di codice in molte linee per scopi di leggibilità o organizzazione. Questo è un JavaScript valido:

var
    name = "bob",
    age = 18;

Quando JavaScript vede un'istruzione incompleta come var , guarda alla riga successiva per completare se stessa. Tuttavia, se si commette lo stesso errore con il return economico, non sarà possibile ottenere quello che vi aspettavate.

return
    "Hi, my name is "+ name + ". " +
    "I'm "+ age + " years old.";

Questo codice tornerà undefined perché return per sé è un'istruzione completa in Javascript, quindi non cercherà di completare la riga successiva. Se hai bisogno di dividere una dichiarazione di return in più righe, inserisci un valore accanto a return prima di dividerlo, in questo modo.

return "Hi, my name is " + name + ". " +
    "I'm " + age + " years old.";

Passare argomenti per riferimento o valore

In JavaScript tutti gli argomenti vengono passati per valore. Quando una funzione assegna un nuovo valore a una variabile argomento, tale modifica non sarà visibile al chiamante:

var obj = {a: 2};
function myfunc(arg){
    arg = {a: 5}; // Note the assignment is to the parameter variable itself
}
myfunc(obj);
console.log(obj.a); // 2

Tuttavia, le modifiche apportate alle proprietà (nidificate) di tali argomenti, saranno visibili al chiamante:

var obj = {a: 2};
function myfunc(arg){
    arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5

Questo può essere visto come una chiamata per riferimento : sebbene una funzione non possa cambiare l'oggetto del chiamante assegnandogli un nuovo valore, potrebbe mutare l'oggetto del chiamante.

Poiché gli argomenti con valori primitivi, come numeri o stringhe, sono immutabili, non c'è modo per una funzione di mutarli:

var s = 'say';
function myfunc(arg){
    arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'

Quando una funzione vuole mutare un oggetto passato come argomento, ma non vuole realmente mutare l'oggetto del chiamante, la variabile argomento deve essere riassegnata:

6
var obj = {a: 2, b: 3};
function myfunc(arg){
    arg = Object.assign({}, arg); // assignment to argument variable, shallow copy
    arg.a = 5;
}
myfunc(obj);
console.log(obj.a); // 2

In alternativa alla mutazione sul posto di un argomento, le funzioni possono creare un nuovo valore, basato sull'argomento, e restituirlo. Il chiamante può quindi assegnarlo, anche alla variabile originale passata come argomento:

var a = 2;
function myfunc(arg){
    arg++;
    return arg;
}
a = myfunc(a);
console.log(obj.a); // 3

Chiama e applica

Le funzioni hanno due metodi incorporati che consentono al programmatore di fornire argomenti e this variabile in modo diverso: call e apply .

Ciò è utile, poiché le funzioni che operano su un oggetto (l'oggetto di cui sono proprietà) possono essere riutilizzate per operare su un altro oggetto compatibile. Inoltre, gli argomenti possono essere forniti in uno scatto come array, in modo simile all'operatore di diffusione ( ... ) in ES6.

let obj = {
    a: 1,
    b: 2,
    set: function (a, b) {
        this.a = a;
        this.b = b;
    }
};

obj.set(3, 7); // normal syntax
obj.set.call(obj, 3, 7); // equivalent to the above
obj.set.apply(obj, [3, 7]); // equivalent to the above; note that an array is used

console.log(obj); // prints { a: 3, b: 5 }

let myObj = {};
myObj.set(5, 4); // fails; myObj has no `set` property
obj.set.call(myObj, 5, 4); // success; `this` in set() is re-routed to myObj instead of obj
obj.set.apply(myObj, [5, 4]); // same as above; note the array

console.log(myObj); // prints { a: 3, b: 5 }
5

ECMAScript 5 ha introdotto un altro metodo chiamato bind() oltre a call() e apply() per impostare esplicitamente this valore della funzione su un oggetto specifico.

Si comporta in modo molto diverso dagli altri due. Il primo argomento di bind() è il this valore per la nuova funzione. Tutti gli altri argomenti rappresentano parametri denominati che devono essere impostati in modo permanente nella nuova funzione.

function showName(label) {
    console.log(label + ":" + this.name);
}
var student1 = {
     name: "Ravi"
};
var student2 = {
     name: "Vinod"
};

// create a function just for student1
var showNameStudent1 = showName.bind(student1);
showNameStudent1("student1"); // outputs "student1:Ravi"

// create a function just for student2
var showNameStudent2 = showName.bind(student2, "student2");
showNameStudent2(); // outputs "student2:Vinod"

// attaching a method to an object doesn't change `this` value of that method.
student2.sayName = showNameStudent1;
student2.sayName("student2"); // outputs "student2:Ravi"

Parametri di default

Prima di ECMAScript 2015 (ES6), il valore predefinito di un parametro poteva essere assegnato nel seguente modo:

function printMsg(msg) {
  msg = typeof msg !== 'undefined' ? // if a value was provided 
        msg :                        // then, use that value in the reassignemnt
        'Default value for msg.';    // else, assign a default value
  console.log(msg);
}

ES6 ha fornito una nuova sintassi in cui la condizione e la riassegnazione illustrate sopra non sono più necessarie:

6
function printMsg(msg='Default value for msg.') {
    console.log(msg);
}
printMsg(); // -> "Default value for msg."
printMsg(undefined); // -> "Default value for msg."
printMsg('Now my msg in different!'); // -> "Now my msg in different!"

Ciò dimostra anche che se un parametro manca quando viene invocata la funzione, il suo valore viene mantenuto undefined , in quanto può essere confermato esplicitamente fornendolo nell'esempio seguente (utilizzando una funzione freccia ):

6
let param_check = (p = 'str') => console.log(p + ' is of type: ' + typeof p);

param_check(); // -> "str is of type: string"
param_check(undefined); // -> "str is of type: string"

param_check(1); // -> "1 is of type: number"
param_check(this); // -> "[object Window] is of type: object"

Funzioni / variabili come valori predefiniti e parametri di riutilizzo

I valori dei parametri predefiniti non sono limitati a numeri, stringhe o oggetti semplici. Una funzione può anche essere impostata come valore predefinito callback = function(){} :

6
function foo(callback = function(){ console.log('default'); }) {
    callback();
}

foo(function (){
    console.log('custom');
});
// custom

foo();
//default

Esistono alcune caratteristiche delle operazioni che possono essere eseguite tramite i valori predefiniti:

  • Un parametro precedentemente dichiarato può essere riutilizzato come valore predefinito per i valori dei parametri imminenti.
  • Le operazioni in linea sono consentite quando si assegna un valore predefinito a un parametro.
  • Le variabili esistenti nello stesso ambito della funzione dichiarata possono essere utilizzate nei suoi valori predefiniti.
  • Le funzioni possono essere invocate per fornire il loro valore di ritorno in un valore predefinito.
6
let zero = 0;
function multiply(x) { return x * 2;}

function add(a = 1 + zero, b = a, c = b + a, d = multiply(c)) {
    console.log((a + b + c), d);
}

add(1);                // 4, 4
add(3);                // 12, 12
add(2, 7);             // 18, 18
add(1, 2, 5);          // 8, 10
add(1, 2, 5, 10);      // 8, 20 

Riutilizzo del valore di ritorno della funzione nel valore predefinito di una nuova chiamata:

6
let array = [1]; // meaningless: this will be overshadowed in the function's scope
function add(value, array = []) {
  array.push(value);
  return array;
}
add(5);         // [5]
add(6);         // [6], not [5, 6]
add(6, add(5)); // [5, 6]

valore degli arguments e lunghezza quando mancano parametri in invocazione

L' oggetto dell'array arguments conserva solo i parametri i cui valori non sono predefiniti, cioè quelli che sono esplicitamente forniti quando la funzione è invocata:

6
function foo(a = 1, b = a + 1) {
    console.info(arguments.length, arguments);
    console.log(a,b);
}

foo();        // info: 0 >> []     | log: 1, 2
foo(4);       // info: 1 >> [4]    | log: 4, 5
foo(5, 6);    // info: 2 >> [5, 6] | log: 5, 6

Funzioni con un numero sconosciuto di argomenti (funzioni variadiche)

Per creare una funzione che accetta un numero indeterminato di argomenti, ci sono due metodi che dipendono dal tuo ambiente.

5

Ogni volta che viene chiamata una funzione, ha un oggetto argomenti Array-like nel suo ambito, che contiene tutti gli argomenti passati alla funzione. L'indicizzazione o l'iterazione di questo darà accesso agli argomenti, ad esempio

function logSomeThings() {
    for (var i = 0; i < arguments.length; ++i) {
        console.log(arguments[i]);
    }
}

logSomeThings('hello', 'world');
// logs "hello"
// logs "world"

Si noti che è possibile convertire gli arguments in una matrice effettiva se necessario; vedere: Conversione di oggetti tipo array in matrici

6

Da ES6, la funzione può essere dichiarata con il suo ultimo parametro usando l' operatore di rest ( ... ). Questo crea una matrice che mantiene gli argomenti da quel punto in poi

function personLogsSomeThings(person, ...msg) {
   msg.forEach(arg => {
       console.log(person, 'says', arg);
   });
}

personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"

Le funzioni possono anche essere chiamate in modo simile, la sintassi di diffusione

const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]

logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]

Questa sintassi può essere utilizzata per inserire un numero arbitrario di argomenti in qualsiasi posizione e può essere utilizzata con qualsiasi iterabile ( apply accetta solo oggetti di tipo array).

const logArguments = (...args) => console.log(args)
function* generateNumbers() {
  yield 6
  yield 5
  yield 4
}

logArguments('a', ...generateNumbers(), ...'pqr', 'b')
// output: Array [ "a", 6, 5, 4, "p", "q", "r", "b" ]

Ottieni il nome di un oggetto funzione

6

ES6 :

myFunction.name

Spiegazione su MDN . A partire dal 2015 funziona in nodejs e tutti i principali browser tranne IE.


5

ES5 :

Se hai un riferimento alla funzione, puoi fare:

function functionName( func )
{
    // Match:
    // - ^          the beginning of the string
    // - function   the word 'function'
    // - \s+        at least some white space
    // - ([\w\$]+)  capture one or more valid JavaScript identifier characters
    // - \(         followed by an opening brace
    //
    var result = /^function\s+([\w\$]+)\(/.exec( func.toString() )
    
    return result ? result[1] : ''
}

Applicazione parziale

Simile al currying, l'applicazione parziale viene utilizzata per ridurre il numero di argomenti passati a una funzione. A differenza del curry, il numero non deve scendere di uno.

Esempio:

Questa funzione ...

function multiplyThenAdd(a, b, c) {
    return a * b + c;
}

... può essere usato per creare un'altra funzione che sarà sempre moltiplicata per 2 e quindi aggiungere 10 al valore passato;

function reversedMultiplyThenAdd(c, b, a) {
    return a * b + c;
}

function factory(b, c) {
    return reversedMultiplyThenAdd.bind(null, c, b);
}

var multiplyTwoThenAddTen = factory(2, 10);
multiplyTwoThenAddTen(10); // 30

La parte "applicazione" dell'applicazione parziale significa semplicemente il fissaggio dei parametri di una funzione.

Composizione funzionale

La composizione di più funzioni in una è una pratica comune di programmazione funzionale;

la composizione crea una pipeline attraverso la quale i nostri dati transitano e si modificano semplicemente lavorando sulla composizione delle funzioni (proprio come far scattare pezzi di una traccia insieme) ...

inizi con alcune funzioni di responsabilità singola:

6
 const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
 const sign = x => x + ',\nmade with love';

e creare facilmente una traccia di trasformazione:

6
 const formatText = compose(capitalize, sign);

 formatText('this is an example')
 //This is an example,
 //made with love

NB La composizione è ottenuta attraverso una funzione di utilità chiamata solitamente compose come nel nostro esempio.

L'implementazione di compose è presente in molte librerie di utilità JavaScript ( lodash , rambda , ecc.) Ma puoi anche iniziare con una semplice implementazione come:

6
 const compose = (...funs) =>
   x =>
   funs.reduce((ac, f) => f(ac), x);


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow