2011-12-17 16 views
9

Leggendo un esempio da un libro, qualcuno può spiegare come la funzione chiamata a fibonacci assuma l'argomento 'i' quando la funzione stessa non dichiara alcun parametro?Spiegazione Memoizzazione Javascript?

var fibonacci = (function() { 
    var memo = [0, 1]; 
    var fib = function (n) { 
     var result = memo[n]; 
     if (typeof result !== 'number') { 
      result = fib(n - 1) + fib(n - 2); 
      memo[n] = result; 
     } 
     return result; 
    }; 
    return fib; 
}()); 

for(var i = 0; i <= 10; i += 1) { 
    document.writeln('// ' + i + ': ' + fibonacci(i)); 
} 

risposta

15

Si sta creando una funzione anonima autoeseguibile (function(){}()); che al suo interno restituisce la funzione fib, che accetta un argomento. var fib = function(n){} ... return fib;

var fibonacci = (function() { // Self-executing anonymous function 
    var memo = [0, 1];   // local variable within anonymous function 
    var fib = function (n) { // actual fib function (takes one argument) 
     var result = memo[n]; 
     if (typeof result !== 'number') { 
      result = fib(n - 1) + fib(n - 2); 
      memo[n] = result; 
     } 
     return result; 
    }; 
    return fib; // return fib (fibonacci is now set to the function fib defined above, which takes one argument) 
}()); 

Questo sistema (il ritorno di una funzione da una funzione anonima self-executing) permette di definire variabili in un ambito locale che può ancora essere utilizzata dalla funzione restituito, ma non da funzioni al di fuori dello scopo. Ecco uno example.

Questa tecnica è denominata closure in JavaScript. Per saperne di più sul MDN guide.

+4

Vale la pena notare che la tecnica della funzione interna che accede alle variabili sulla funzione outter anche dopo che la funzione outter è ritornata, è conosciuta come 'closure' – 7hi4g0

+0

Questo è dal libro Javascript - Le parti buone e c'è una migliore descrizione del processo lì. –

2
var fibonacci = (function() { 
    ... 
    return fib; 
})(); 

Questa è una funzione self-executing.

Si dichiara un'espressione di funzione che restituisce una funzione (fib), esegue immediatamente l'espressione funzione esterna (()) e assegna il valore restituito (che è fib) alla variabile fibonacci.

1

La funzione fibonacci accetta un argomento. Si noti che la funzione senza nome che inizia sulla prima riga non è la funzione che diventa nota come fibonacci. Quella funzione senza nome viene immediatamente chiamata poiché hai () immediatamente dopo aver chiuso la parentesi graffa }. Questa funzione senza nome restituisce fib, una variabile locale a cui è assegnata una funzione a argomento singolo. Così, fibonacci finisce riferimento alla funzione restituita dalla funzione senza nome, cioè fibonacci è questo:

var fib = function (n) { 
    var result = memo[n]; 
    if (typeof result !== 'number') { 
     result = fib(n - 1) + fib(n - 2); 
     memo[n] = result; 
    } 
    return result; 
}; 

noti che questa funzione si riferisce a variabili locali della funzione anonima al fine di memoizing.

La cosa importante da notare è stato () che chiama la funzione senza nome subito, ecco alcuni esempi che illustrano questa tecnica:

var a = (function() { return 1; }); 

variabile a detiene una funzione che restituisce 1.

var a = (function() { return 1; }()); 

Qui tuttavia, la variabile a contiene il valore 1.

6

Perché la funzione restituisce una funzione che fa accetta un parametro.

3

C'è una funzione di auto-chiamata che restituisce la funzione con l'identificatore fib che viene poi assegnato al identificatore fibonacci. In questo modo è possibile creare una variabile privata memo che è accessibile solo dalla funzione. Quindi var fibonacci infatti è function(n){...}.

5

Per capire questo, penso che sia utile lavorare con un esempio più semplice. Dai un'occhiata alle due funzioni memorizzate qui sotto. L'unica differenza è () dopo il add : function(){ ... }() sul codice di memorizzazione riuscito.

var failed_memoization = { 
     add : function(){ 
      var counter; 
      return function(number){ 
       if(counter){ 
        counter = counter + number; 
        return counter; 
       } 
       counter = number; 
       return counter; 
      } //NOTE: NO function call brackets here 
     } 
    } 

    var successful_memoization = { 
     add : function(){ 
      var counter; 
      return function(number){ 
       if(counter){ 
        counter = counter + number; 
        return counter; 
       } 
       counter = number; 
       return counter; 
      } 
     }() //NOTE: the function call brackets here!! 
    }; 
} 

Ora eseguiamo queste due funzioni.

console.log('Failed Memoization'); 
console.log(failed_memoization.add(5));  //We wanted 5, but this prints the text of the function instead.... Okay, lets try something else 
console.log(failed_memoization.add()(5)); //5 
console.log(failed_memoization.add()(10)); //10 (Wanted it to be 5+10 = 15. 
console.log('successful_memoization'); 
console.log(successful_memoization.add(8)); //8 
console.log(successful_memoization.add(16)); //24 (This is what we wanted 8 + 16 = 24) 

Allora, cosa sta succedendo qui è che per successful_memoization quando abbiamo messo () alla fine della sua add : function(){...}(). In quanto tale, questa funzione viene eseguita immediatamente alla creazione dell'oggetto statico. A sua volta, l'esecuzione di tale funzione restituisce l'oggetto function (number){...} a cui corrisponde l'assegnazione: add : function (number){...}NONadd : function(){} come inizialmente appare.

Ciò che è anche importante notare è che var counter è dichiarato all'esternoreturn function(name){}. Poiché è ancora in uso all'interno di add : function(number){...}, questa variabile è accessibile all'interno di quella funzione. Per failed_memoization.add()(number), utilizza un nuovo counter ogni volta che eseguiamo quella funzione, perché eseguiamo la prima funzione e quindi la funzione interna su ogni chiamata. Per successful_memoization.add(number) abbiamo eseguito la funzione esterna al momento dell'inizializzazione, quindi counter verrà mantenuta per tutte le chiamate successive e non verrà sovrascritta.