2010-07-12 18 views
12

È possibile ridefinire una funzione JavaScript all'interno del proprio corpo. Ad esempio, potrei fare quanto segue?ridefinizione della funzione JavaScript

function never_called_again(args) { 
    // Do some stuff 
    never_called_again = function (new_args) { 
    // Do some new stuff 
    } 
} 

È valido quanto sopra e ha la semantica corretta? Non voglio creare una nuova variabile globale con il nome della vecchia funzione, perché sto provando a fare questo genere di cose non nell'ambito globale, ma da vari ambiti di oggetti, e non voglio conflitti di nome quando ridefinire la funzione all'interno di quegli ambiti locali.

+2

Non ho idea se sia possibile o meno ma non sembra una cosa da fare. Ovviamente non so quali sono le tue circostanze, ma sembra che abbia un grande potenziale di confusione, bug difficili da rintracciare e codice difficile da leggere. Ci penserei seriamente se questa è la soluzione giusta o se vuoi fare il backup di un passo e porre la domanda che ti ha portato a questo come risposta. Scusa se hai già considerato tutte le opzioni. Non si può mai dire quello che qualcuno ha thoguth su già o no ... – Chris

+5

c'è un uso legittimo per questo, e questa è una cosa potente. Le persone hanno paura di ciò che non sanno, ma questo non significa che siano cattivi. – galambalazs

+0

Si noti che questa tecnica in realtà non ridefinisce la funzione, cambia la funzione "never_called_again" a cui fa riferimento.La funzione originale non scompare necessariamente, dato che (anche se non lo fai qui) è possibile creare un'altra variabile che faccia riferimento alla funzione originale: metti 'original = never_called_again; 'prima di riassegnare' never_called_again' e 'original () 'invocherà l'originale ... – nnnnnn

risposta

14

Sì, è possibile ridefinire una funzione in questo modo.

corso Questo:

function never_called_again(args) { 
    console.log('Func 1', args); 

    never_called_again = function (new_args, x) { 
     console.log('Func 2', new_args, x); 
    } 
} 

never_called_again('arg A', 'arg B'); 

never_called_again('arg A', 'arg B'); 

cede questo:

Func 1 arg A 
Func 2 arg A arg B 
+1

@galambalazs: ti ho dato credito e ho ampliato aspetti che non coprivi per la mia soddisfazione. Inoltre, la mia prima risposta e risposta principale non aveva nulla a che fare con la tua ed era sufficiente. –

+1

Non è un wiki della comunità **. Le variabili globali non hanno nulla a che fare con la ridefinizione delle funzioni, sono solo correlate a Lazy Func. Pattern di definizione, che è un esempio per la ridefinizione della funzione. Tutto è spiegato bene nel link btw ... Ma ancora una volta era solo un esempio. La tua risposta è stata serrata e senza voti è per questo che l'hai cambiata ... – galambalazs

+1

@galambalazs: No, prima che lo cambiassi, avevo tanti voti quanti ne hai (1 ciascuno) - prima che io avessi svalutato. Ho cambiato la mia risposta per renderla più completa, incorporando le lezioni tratte dall'articolo che hai citato. Inoltre ti ho dato credito. Ho visto altre persone farlo. Dovrebbe riguardare il miglioramento delle risposte, non una stupida sborra. Secondo la tua logica, Peter Michaux dovrebbe seguirti per plagio. Ad ogni modo, ho ripristinato la mia risposta –

2

Il vostro esempio è fondamentalmente lo stesso come:

var never_called_again = function(args) { 
    //do some stuff 
    never_called_again = function (new_args) { 
     //do some new stuff 
    } 
} 

Dal momento che probabilmente sapete che questo funziona:

var a = 1; 

function myFunction() { 
    //do some stuff 
    a = 2; 
} 

myFunction(); 

alert(a) //==> 2 

è chiaro che il primo esempio funziona troppo.

Se in realtà lo vuole fare questo o no è una domanda più aperta. Rende il codice più difficile da capire?

40

È infatti possibile ridefinire una funzione dal corpo. La tecnica è utilizzata nel cosiddetto Lazy Function Definition Pattern.

Ecco come si presenta:

// Lazy Function Definition Pattern 
var foo = function() { 
    var t = new Date(); 
    foo = function() { 
     return t; 
    }; 
    return foo(); 
} 

Questa funzione memorizza la data della prima chiamata, e restituisce questa data dopo.

Se si confrontano questo con il modello di modulo di , la differenza è che l'inizializzazione avviene solo quando viene prima chiamato, non quando è definito. Per inizializzazioni costose può essere un enorme vantaggio .

// Conditional Module Pattern 
var foo = (function() { 
    var t; 
    return function() { 
     if (t) { 
      return t; 
     } 
     t = new Date(); 
     return t; 
    } 
})(); 
+0

Bello. snip 8 <--- – mplungjan

+0

Grazie per il link è stato piuttosto interessante e sto usando il pattern in un modo simile per evitare vari percorsi di esecuzione condizionale. – davidk01

1

Sì, è possibile e non creerà una funzione globale. Ho verificato questo in Internet   Explorer   6, Firefox, Chrome e Opera. Si consideri il seguente codice:

<head> 
    <title>Never called again</title> 
    <style type="text/css"> 
    </style> 

    <script type="text/javascript"> 

      function hello() { 
      function never_called_again(args) { 
       alert("Hello world " + never_called_again); 
       //do some stuff 
       never_called_again = function (new_args) { 
        //do some new stuff 
        alert("hello " + never_called_again); 
       } 
      } 
      never_called_again(); 
      never_called_again(); 

      } 

     </script> 

    </head> 
    <body onload=""> 

    <button onclick="hello(); never_called_again();">Call</button> 
    </body> 

Questo stamperà "Ciao Mondo {} codice di funzione" per la prima volta del never_called_again e la seconda volta che stamperà "ciao {codice funzione cambiato}" la seconda volta.

Ma quando viene richiamato sul pulsante onclick, viene generato un errore che indica che la funzione non è definita, indicando chiaramente che la funzione è stata ridefinita (e non creata globalmente).

3

È totalmente possibile ridefinire un oggetto funzione in qualsiasi cosa si desideri alla prima chiamata.

Il motivo è possibile perché le valutazioni dell'assegnazione vengono gestite da destra a sinistra. Ciò significa che l'ambito della funzione viene catturato quando lo si esegue (in questo caso, quando si chiama la funzione).

Ciò significa che al momento dell'esecuzione, l'oggetto funzione che viene eseguito e la definizione della funzione originale sono in realtà due cose diverse, che consente la ridefinizione della funzione originale come, beh ... qualsiasi cosa realmente.

Uno dei modi migliori (e più interessanti) per utilizzarlo è la creazione di una funzione che è, in sostanza, una propria fabbrica. In questo modo non devi portare in giro molte definizioni di oggetti che non verranno utilizzate.

Esempio:

d = function(type){ 
    switch(type.toUpperCase()){ 
     case 'A' : 
      d = (
        function(){ 
         return { 
          name : 'A', 
          getName : function(){return this.name;} 
         }; 
        } 
       )(); 
       break; 

     case 'B' : 
      d = (
       function(){ 
        return { 
         name : 'B', 
         getName : function(){return this.name;} 
        }; 
       } 
      )(); 
      break; 

     default: 
      d = (
       function(){ 
        return { 
         name : 'DEFAULT', 
         getName : function(){return this.name;} 
        }; 
       } 
      )(); 
      break; 
    } 
}; 

console.dir(d); 
d('A'); 
console.dir(d); 
console.log(d.getName()); 
1

in senso stretto, no, non è possibile ridefinire una funzione JavaScript a tutti. Anche se è una questione di semantica.

Una funzione può sovrascrivere il proprio riferimento all'interno di un determinato ambito con un'altra funzione, che funge da gioco di prestigio dove un'altra funzione appare nel posto del primo. per esempio.

greet_once = function() { 
    console.log('Hello.'); 
    greet_once = function() { console.log('Can I help you?') }; 
}; 

Analogamente una funzione può utilizzare lo stato di un ambito closured comportarsi in modo diverso la seconda volta la sua chiamata (ovviamente non "ridefinizione").

L'esempio seguente illustra il motivo per cui il modello precedente non è la ridefinizione poiché dipende dall'ambito padre che non può essere condiviso con l'ambito chiamante.

greet_always = function() { 
    greet_once = function() { 
    console.log('Hello.') 
    greet_once = function() { console.log('Can I help you?') } 
    }; 
    return greet_once() 
}() 

greet_always() 
greet_always() 

La funzione di essere chiamato come greet_always (che sempre stampe 'Ciao.') È lo stesso, assegnato in origine a greet_once.

Il più vicino possibile a ridefinire una funzione è riassegnare i metodi apply e call, che produce uno scenario in cui myFunction.call() è diverso da myFunction(), che è strano ma teoricamente utile.

Problemi correlati