2011-01-27 8 views
17

Estoy tratando de llamar a una función dentro de un objeto literal que creé, usando la palabra clave this. Sin embargo, un error aparece diciendo this.doTheMove() no es una función:Llamar a una función en javascript object declaración de notación literal

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!'); 

    } 
} 

¿Por qué hay un error?

Respuesta

16

Una explicación de lo que está pasando. La respuesta de Pointy es buena, pero quiero explicarlo de manera más genérica. Se puede encontrar una muy buena investigación en thishere

Un controlador de eventos es solo una devolución de llamada. Le pasas una función y un evento para escuchar. Interalmente, todo lo que hará es llamar a esa función.

Animation.init es simplemente un getter para esa función. Piense en ello como esto:

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(); 
} 

Así que todo lo que has hecho se pasa en

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

Por defecto en javascript this === window. Esto se referirá al objeto global si no está configurado en algo. El efecto neto es que se llama window.doTheMove. Y esa función no existe.

En este caso, ya callback se llama convenientemente indicado por un Gestor de Eventos this los puntos del objeto en el objeto DOM que ha activado el evento para que su llamada node.doTheMove que todavía no existe.

Lo que quería hacer es envolverlo con una referencia a la animación.

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

Esta es una ejecución de la función y se ejecuta en initAnimation. Cuando lo ejecuta en un objeto como ese, internamente this === Animation como era de esperar.

En resumen. El problema aquí es que Animation.init es solo una referencia a una función. No tiene información sobre nada más, como mencionó Pointy.

+3

Su respuesta es en su mayoría correcta (absolutamente correcta en el resultado final). El único error es * "El efecto neto es que se llama a window.doTheMove." *. Como 'init' se pasa a' addEventListener', en realidad se está llamando desde el contexto del elemento 'animBtn', por lo que' this.doTheMove() 'es efectivamente' animBtn.doTheMove() '. Nuevamente, el resultado final es el mismo. Solo un pequeño detalle. – user113716

+0

@patrickdw Gracias, no estaba seguro de si 'this' se pasó como el objeto DOM o el objeto Window. No me molesté en probarlo para confirmarlo. Lo esperaba tanto. – Raynos

+1

+1 Y, para ser justos, su evaluación de 'this' hubiera sido correcta si se hubiera utilizado' attachEvent' de Microsoft, ya que no establece (por algún motivo) el contexto del controlador. – user113716

10

Hay que cambiar la forma de poner esto en marcha:

window.onload = function(){ 

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

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

} 

En JavaScript, el hecho de que una función pasa a ser definido como parte de un objeto literal que realmente no significa mucho (o nada , de hecho). La referencia a Animation.inithace que te lleve a la función adecuada, pero el problema es que cuando la función se invoca más tarde (en respuesta a un "clic" real), el navegador llama a la función pero no tiene idea de que el objeto "Animación "debería ser la referencia this. Nuevamente, el hecho de que la función fue declarada como parte del objeto no tiene importancia aquí. Por lo tanto, si quiere que this sea algo en particular de su propia elección, entonces debe asegurarse de que esté establecido explícitamente en el código que controla. La solución anterior es sobre la forma más sencilla de hacerlo: maneja los eventos de "clic" con una función anónima que no hace más que invocar la función "init" a través de una referencia explícita a través de "Animación". Eso asegurará que this se refiere al objeto "Animación" cuando se ejecuta "init".

Otra alternativa sería utilizar el ".bind) (" instalación que algunos navegadores y marcos de soporte:

window.onload = function(){ 

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

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

} 

El efecto neto es casi exactamente el mismo: que la llamada a ".bind()" devuelve una función que invoca la función a la que se llamó (esa es la función "init" en el objeto "Animación"), y lo hace con su primer argumento como la referencia this (el objeto "context"). Es lo mismo que obtenemos del primer ejemplo, o efectivamente lo mismo de todos modos.

+0

explique con más detalle cómo pasar el puntero de la función Animation.init y llamarlo lo invoca con un objeto 'this' que no es' Animation'. La solución es buena, pero falta la explicación de por qué falla en primer lugar. – Raynos

+0

@Raynos sí, tienes razón, añadiré un texto para describir eso. – Pointy

+0

Gracias por su respuesta, lo probaré ahora – Shaoz

0

Es posible que desee utilizar el enfoque de 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

Esto no es correcto. El 'addEventListener' necesita recibir una función. Está pasando un objeto que hace referencia a una función. – user113716

+0

¡Correcto! mi respuesta es incorrecta, sry. – Spliffster

1

Aquí hay otro enfoque agradable, creo.

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

Seis años y medio después, pero espero que mi respuesta también pueda proporcionar una idea para los desarrolladores actuales y futuros.

Tiendo a codificar usando objetos literales dentro de las funciones definidas por uno mismo, y la pregunta original publicada funciona bien si se agrega otra función autoejecutable junto con una declaración try y catch.

Es muy importante señalar que se trata de alcance y contexto.

Corrija los inconvenientes o proporcione sugerencias más eficaces sobre el uso de este método.

(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); 
     } 
    })(); 

})(); 
Cuestiones relacionadas