2011-01-27 28 views
17

Sto provando a chiamare una funzione all'interno di un oggetto letterale che ho creato, utilizzando la parola chiave this. Ma un errore si presenta dicendo this.doTheMove() non è una funzione:Chiamare una funzione nella dichiarazione di notazione letterale dell'oggetto javascript

window.onload = function(){ 

    var animBtn = document.getElementById('startAnim'); 

    animBtn.addEventListener('click', Animation.init, false); 

} 

var Animation = { 
    init: function(){ 

    this.doTheMove(); // I'm calling the function here, but it gives an error. 

    }, 
    doTheMove: function(){ 

    alert('Animation!'); 

    } 
} 

Perché c'è un errore?

risposta

16

Una spiegazione di ciò che sta accadendo. La risposta di Pointy è buona, ma voglio spiegarla in modo più generico. Un'ottima ricerca su this può essere trovata here

Un gestore di eventi è solo un callback. Si passa una funzione e un evento per ascoltare. Interamente tutto ciò che farà è chiamare quella funzione.

Animation.init è solo un getter per quella funzione. Pensate a come questo:

var callback = Animation.init 
animBtn.addEventListener('click', callback, false); 
... 
// internal browser event handler 
handler() { 
    // internal handler does stuff 
    ... 
    // Oh click event happened. Let's call that callback 
    callback(); 
} 

Quindi tutto quello che hai fatto è passato in

var callback = function(){ 
    this.doTheMove(); // I'm calling the function here, but it gives an error. 
} 

Per impostazione predefinita in javascript this === window. Questo si riferirà all'oggetto globale se non è impostato su qualcosa. L'effetto netto è che viene chiamato window.doTheMove. E quella funzione non esiste.

In questo caso, poiché callback è actaully chiamato da un evento Handler i punti oggetto this presso l'oggetto DOM che ha attivato l'evento in modo che le chiamate node.doTheMove che ancora non esiste.

Quello che volevi fare è racchiuderlo con un riferimento all'Animazione.

var callback = function() { 
    Animation.init(); 
} 

Questa è una funzione di esecuzione ed esegue init su Animation. Quando lo esegui su un oggetto del genere allora internamente this === Animation come ti aspetteresti.

Per riassumere. Il problema qui è che Animation.init è solo un riferimento a una funzione. Non ha informazioni su nient'altro come Pointy menzionato.

+3

La tua risposta è per la maggior parte corretta (assolutamente corretta nel risultato finale). L'unico errore è * "L'effetto netto è che window.doTheMove viene chiamato." *. Poiché 'init' è passato a' addEventListener', viene effettivamente chiamato dal contesto dell'elemento 'animBtn' così' this.doTheMove() 'è effettivamente' animBtn.doTheMove() '. Ancora una volta, il risultato finale è lo stesso. Solo un piccolo dettaglio – user113716

+0

@patrickdw Grazie non ero sicuro se 'this' è stato trasmesso come oggetto DOM o oggetto Window. Non mi sono preoccupato di provarlo per confermare. Mi aspettavo tanto. – Raynos

+1

+1 E per essere onesti, la tua valutazione di 'this' sarebbe stata corretta se fosse stato utilizzato' attachEvent' di Microsoft poiché non ha (per qualche motivo) impostato il contesto del gestore. – user113716

10

Dovete cambiare il modo in cui si imposta che fino:

window.onload = function(){ 

    var animBtn = document.getElementById('startAnim'); 

    animBtn.addEventListener('click', function() { Animation.init(); }, false); 

} 

In JavaScript, il fatto che una funzione sembra essere definito come parte di un oggetto letterale in realtà non significa molto (se non altro , infatti). Il riferimento a Animation.initfa per ottenere la funzione corretta, ma il problema è che quando la funzione viene successivamente richiamata (in risposta a un "clic" effettivo), il browser chiama la funzione ma non ha idea che l'oggetto "Animazione" "dovrebbe essere il riferimento this. Ancora una volta, il fatto che la funzione sia stata dichiarata come parte dell'oggetto non ha assolutamente alcuna importanza qui. Pertanto, se vuoi che lo this sia qualcosa in particolare di tua scelta, devi assicurarti che sia impostato in modo esplicito nel codice che controlli. La soluzione di cui sopra è il modo più semplice per farlo: gestisce gli eventi "click" con una funzione anonima che non fa altro che invocare la funzione "init" tramite un riferimento esplicito tramite "Animazione". Ciò garantirà che this si riferisca all'oggetto "Animazione" quando viene eseguito "init".

Un'altra alternativa sarebbe quella di utilizzare il ".bind) (" struttura che alcuni browser e quadri supporto:

window.onload = function(){ 

    var animBtn = document.getElementById('startAnim'); 

    animBtn.addEventListener('click', Animation.init.bind(Animation); }, false); 

} 

L'effetto netto è quasi esattamente la stessa: la chiamata a ".bind()" restituisce una funzione che richiama la funzione su cui è stata chiamata (che è la funzione "init" nell'oggetto "Animazione"), e lo fa con il suo primo argomento come riferimento this (l'oggetto "contesto"). Questa è la stessa cosa che otteniamo dal primo esempio, o effettivamente lo stesso comunque.

+0

spiegare in modo più dettagliato come passare il puntatore della funzione Animation.init e richiamarlo invocandolo con un oggetto 'questo' che non è' Animazione'. La soluzione è buona, ma manca la spiegazione del perché fallisce. – Raynos

+0

@Raynos si hai ragione, aggiungerò del testo per descriverlo. – Pointy

+0

Grazie per la tua risposta, lo proverò ora – Shaoz

0

Si potrebbe desiderare di utilizzare l'approccio di base portotype:

// generate a prototype object which can be instantiated 
var Animation = function() { this.doTheMove(); } 
Animation.prototype.doTheMove = function() { 
    // if the object has only one method, the whole code could be moved to 
    // var Animation = function() {...} above 
    alert('Animation!'); 
} 

Animation.prototype.otherMethod = function(param1, param2) { 
    // ... 
} 


// run the code onload 
window.onload = function(){ 
    var animBtn = document.getElementById('startAnim'); 
    animBtn.addEventListener('click', new Animation(), false); 
} 
+3

Questo non è corretto. 'AddEventListener' ha bisogno di ricevere una funzione. Stai passando un oggetto che fa riferimento a una funzione. – user113716

+0

Giusto! la mia risposta è sbagliata, sry. – Spliffster

1

Ecco un altro buon approccio, penso.

anni
window.onload = function(){ 

    var animBtn = document.getElementById('startAnim'); 

    animBtn.addEventListener('click', Animation.init, false); 

}; 

var Animation = { 
    init: function(){ 

     Animation.doTheMove(); // This will work, but your IDE may complain... 

    }, 
    doTheMove: function(){ 

     alert('Animation!'); 

    } 
}; 
0

sei anni e mezzo più tardi, ma sto sperando che la mia risposta può anche fornire qualche informazione per gli sviluppatori attuali e futuri.

Tendo a codificare utilizzando oggetti letterali all'interno di funzioni autodefinite e la domanda originale postata funziona bene se viene aggiunta un'altra funzione autoesecutiva insieme a un'istruzione try and catch.

È molto importante sottolineare che si tratta di ambito e contesto.

Correggere eventuali inconvenienti o fornire suggerimenti più efficaci sull'uso di questo metodo.

(function() { 

console.log(this); // window object 

    var animation = { 
     init: function() { 
      this.doTheMove(); 
     }, 
     doTheMove: function() { 
      alert("Animation"); 
      console.log(animation); // animation object 
     } 
    }; 

    (function() { 
     try { 
      console.log("animation.init"); // animation.init function 
      animation.init(); 
     } catch(e) { 
      console.log("Error is: " + e); 
     } 
    })(); 

})(); 
Problemi correlati