2009-05-24 19 views
5

Cuando paso 'esto' a una función anónima, así:¿Cómo se transmiten los datos a funciones anónimas en JavaScript?

MyClass.prototype.trigger = function(){ 
    window.setTimeout(function(){this.onTimeout();},1000); 
} 

me sale un "this.onTimeout no es una función" -error. Supongo que 'esto' ya no está disponible en el momento en que se está ejecutando la función anónima. Así que he estado haciendo esto:

MyClass.prototype.trigger = function(){ 
    var me = this 
    window.setTimeout(function(){me.onTimeout();},1000); 
} 

¿Es así como se supone que debes hacer las cosas? Funciona un poco, pero se siente raro.

entonces tenemos este ejemplo:

$(function(){ 
    function MyClass(){ 
     this.queue = new Array(); 
    } 
    MyClass.prototype.gotAnswer = function(count){ 
     $('body').append("count:"+count+"<br/>"); 
    } 
    MyClass.prototype.loadAll = function(){ 
     var count = 0; 
     var item; 
     while(item = this.queue.pop()){ 
      count++; 
      var me = this; 
      $.getJSON("answer.html",{},function(data){me.gotAnswer(count);}); 
     } 
    } 

    var o = new MyClass(); 
    o.queue.push(1); 
    o.queue.push(2); 
    o.loadAll(); 

}); 

Este salidas:

2 
2 

¿No debería de salida:

1 
2 

en su lugar? Entonces descubrí que poner el $ .getJSON-declaración en otra función que hace todo el trabajo:

MyClass.prototype.loadAll = function(){ 
    var count = 0; 
    var item; 
    while(item = this.queue.pop()){ 
     count++; 
     this.newRequest(count); 
    } 
} 
MyClass.prototype.newRequest = function(count){ 
    var me = this; 
    $.getJSON("answer.html",null,function(data){ me.gotAnswer(count); }); 
} 

Este salidas: (. O al revés)

1 
2 

¿Qué está pasando aquí? ¿Cuál es la forma correcta de pasar variables a una función anónima?

Lo siento por la publicación confusa y larga.

Respuesta

5

Lo que está experimentando es el comportamiento correcto, no es un buen comportamiento, pero es parte del lenguaje. El valor de "esto" se restablece dentro de cada definición de función. Hay cuatro formas de llamar a una función que tiene diferentes formas de configurar "esto".

  1. La invocación de función normal
    myFunc(param1, param2);
    Esta forma de llamar a una función siempre restablecerá "esto" al objeto global. Eso es lo que está sucediendo en tu caso.
  2. Llamarlo como un método
    myObj.myFunc(param1, param2);
    Esto, como era de esperar, establece "esto" para cualquier objeto que se invoca el método. Aquí, "esto" == "myObj".
  3. Aplicar método invocación
    myFunc.apply(myObj, [param1, param2])
    Esto es interesante: aquí "esto" se establece en el objeto que pasa como el primer parámetro para el método apply - es como llamar a un método en un objeto que no tiene ese método (tenga cuidado que la función está escrita para ser llamada de esta manera).Todas las funciones por defecto tienen el método de aplicar.
  4. Como constructor (con "nuevo")
    myNewObj = new MyConstructor(param1, param2);
    Cuando llama a una función de esta manera, "esto" se inicializa a un nuevo objeto que hereda métodos y propiedades de la propiedad de prototipo de su función. En este caso, el nuevo objeto heredará de MyConstructor.prototype. Además, si no devuelve un valor explícitamente, se devolverá "this".

La solución que utilizó es la solución recomendada: asigne el valor exterior de "esto" a otra variable que seguirá estando visible dentro de su función. Lo único que cambiaría es llamar a la variable "eso", como dice Török Gábor, esa es una especie de estándar de facto y podría hacer que su código sea más fácil de leer para otros programadores.

+0

¡Genial! ¡Muchas gracias! Hm, creo que en realidad estoy bastante cómodo con cómo funciona js. Es un poco como LUA. Es solo que sigo pensando "Java" y eso realmente no se aplica ... – 0scar

3

Está confundido acerca de los cierres.

Para el primer problema, sí, tienes razón, esa es la forma en que se puede hacer. La única diferencia es que existe una convención para nombrar la variable that que contiene this.

MyClass.prototype.trigger = function(){ 
    var that = this; 
    window.setTimeout(function(){that.onTimeout();},1000); 
} 

Ya hay un buen hilo sobre esto en StackOverflow. Verifique las respuestas a la pregunta How does a javascript closure work?.

Su segundo problema es un duplicado exacto de Javascript closure inside loops - simple practical example.

+0

Gracias d00d! Eso fue todo:) – 0scar

0

tendrá el mismo problema si está dentro de su nuevo método: newRequest tiene que usar una instrucción "for" o "while". Otra solución podría ser la creación de un cierre:

así:

$.getJSON("answer.html",{},(function(me){return function(data){me.gotAnswer(count);}})(this)); 
Cuestiones relacionadas