2012-05-09 23 views
5

Pensé que tenía una comprensión razonable del objeto this en JavaScript. Cuando trato con objetos, devoluciones de llamada, y eventos y manejadores, no he tenido problemas con él desde tiempos inmemoriales. Ahora, sin embargo, todo ha cambiado.Cierre de JavaScript y este objeto

Me he enamorado locamente de JavaScript. JS puro, es decir, no jQuery, prototype.js, dojo ... Así que, naturalmente, me he acostumbrado a usar cierres. En algunos casos, sin embargo, this me está tomando por sorpresa aquí. Tome este fragmento de uno:

function anyFunc(par) 
{ 
    //console.log(par); 
    console.log(this); 
} 

function makeClosure(func) 
{ 
    return function(par) 
    { 
     return func(par); 
    } 
} 
var close = makeClosure(anyFunc); 
close('Foo'); 

var objWithClosure = {cls:makeClosure(anyFunc),prop:'foobar'}; 
objWithClosure.cls(objWithClosure.prop); 

var scndObj = {prop:'Foobar2'}; 
scndObj.cls = makeClosure; 
scndObj.cls = scndObj.cls(anyFunc); 
scndObj.cls(scndObj.prop); 

En los tres casos, this registros como el objeto de la ventana. Es una solución fácil, por supuesto:

function makeClosure(func) 
{ 
    return function(par) 
    { 
     return func.call(this,par); 
    } 
} 

Esta corrección funciona, lo pongo aquí para evitar personas que responden esto, sin explicar lo que necesito saber: ¿por qué es esto comporta como lo hace aquí?

asegura que la persona que llama es efectivamente el objeto al que pertenece el cierre. Lo que no entiendo es esto: Efectivamente, this apunta al objeto ventana en el primer caso, pero en otros casos, no debería. Intenté iniciar sesión en this en la función makeClic justo antes de volver, y se registró el objeto en sí, no el objeto window. Sin embargo, cuando se utiliza el cierre real, this ha vuelto a señalar al objeto de ventana. ¿Por qué?

Lo único que se me ocurre es que, pasando la función anyFunc como argumento, en realidad estoy pasando window.anyFunc. Así que he intentado esta solución rápida:

function makeClosure(func) 
{ 
    var theFunc = func; 
    return function(par) 
    { 
     theFunc(par); 
    } 
} 

con los resultados esperados, this ahora apunta a los objetos, pero una vez más: ¿Por qué? Tengo algunas ideas (theFunc es una referencia a la función en el ámbito local [this > private: theFunc]?), Pero estoy seguro de que hay personas aquí con mucho más conocimiento cuando se trata de JS, así que estaba esperando obtener una explicación más o enlaces a artículos que vale la pena leer de ellos ...

Gracias

actualización

Here's a fiddle, puede ser que dejé algo, pero aquí esta registra todo tipo de cosas;)

E dit/Update 2

The case that confuses me es aquí.

edición final

Ok, Esto se está poniendo un mensaje en vez desordenado. Así que para aclarar: Lo que esperaba era un comportamiento similar a esto:

function makeClosure() 
{ 
    function fromThisFunc() 
    { 
     console.log(this); 
    } 
    return fromThisFunc; 
} 

var windowContext = makeClosure(); 
windowContext(); 
var objectContext = {cls:makeClosure()}; 
objectContext.cls(); 

Lo que me llamó, fue que la función anyFunc no fue declarado dentro del ámbito correcto, y por lo tanto, this señaló al objeto de ventana. Descubrí esto leyendo un ancient scroll que encontré en algún lugar de la web.

Pero algo un poco más complicado ha sucedido porque el objeto función que ahora se conoce por globalVar fue creado con un [[alcance]] propiedad en referencia a una cadena de ámbito contiene el objeto de activación/variable que pertenece al contexto de ejecución en que fue creado (y el objeto global). Ahora el objeto Activación/Variable no se puede recolectar como basura, ya que la ejecución del objeto de función referido por globalVar necesitará agregar toda la cadena de alcance desde su propiedad [[scope]] al alcance del contexto de ejecución creado para cada llamada a eso.

Así que lo que tenía que hacer, fue simplificar en vez de complicar las cosas:

function fromThisFunc() 
{ 
    console.log(this); 
} 

function makeClosure(funcRef) 
{ 
    //some code here 
    return funcRef; 
} 

que debería funcionar, ¿verdad?

PD: Excepto la respuesta de Alnitak, pero un agradecimiento especial a Félix Kling por toda la paciencia y la información.

+0

realmente no conseguir lo más respuesta que un enlace a una explicación global de los cierres podrían hacerse. Este curso es una lectura excelente y debe eliminar todas las incertidumbres: http://ejohn.org/apps/learn/ –

+0

El problema no es que no obtenga cierres. Lo que está causando los dolores de cabeza es que no estoy del todo seguro de lo que le sucede al objeto 'this' al hacer cierres de la manera en que lo hago aquí –

+2

No puedo reproducir el comportamiento que describes. Obtengo 'DOMWindow',' Object', 'Object': http://jsfiddle.net/GErkX/, mientras que tu segundo" fix "me da 3 x' DOMWindow': http://jsfiddle.net/tZXQF/ –

Respuesta

4

Tan pronto como se llame:

return func(par); 

va a crear un nuevo ámbito de aplicación (con su propio this) y en este caso porque no se ha especificado un objeto, por lo general this === windowo indefinido en Modo estricto. La función llamada no contiene heredará this en el alcance de la llamada.

formas de establecer un valor para este son:

myobj.func(par); // this === myobj 

o

func.call(myobj, ...) // this === myobj 

También son:

+1

o -aparentemente- crea una var local en la función 'makeClosure', y asigna el parámetro func a esa variable ... también,' return func (par) 'es el cuerpo de la función, de una función que se devuelve a un objeto , así que asumí que la llamada a 'func (par)' se haría en el contexto del objeto. ¿Por qué no? –

+0

@EliasVanOotegem Todavía estoy tratando de entender el caso anterior ... No puedo replicarlo localmente. – Alnitak

+2

@EliasVanOotegem: Creo que tus pruebas son defectuosas de alguna manera, especialmente la última. Siempre que realice una llamada a la función simplemente 'foo()', 'this' se referirá al objeto global, sin importar dónde se definió la función. ¿En qué entorno estás probando el código? Crear una variable local no hace la diferencia en este caso. El parámetro es local también. Su primer arreglo funciona bien aquí: http://jsfiddle.net/GErkX/ –

1

El valor de this depende sólo de si se llama a la función como un método o como una función .

Si se llama como un método, this será el objeto que el método pertenece a:

obj.myFunction(); 

Si llama como una función, this será el window objeto:

myFunction(); 

Tenga en cuenta que incluso si está en un método que pertenece a un objeto, igual debe llamar a otros métodos en el objeto utilizando la sintaxis del método, de lo contrario se llamarán como funciones:

this.myOtherFunction(); 

Si se pone una referencia al método en una variable, que será separarlo del objeto, y será llamado como una función:

var f = obj.myFunction; 
f(); 

Los métodos call y apply se utilizan para llamar a una funcionan como un método incluso si no es un método en el objeto (o si se trata de un método en un objeto diferente):

myFunction.call(obj); 
+0

gracias, pero realmente no es una respuesta a esta pregunta ... Actualmente uso 'call', pero quiero saber por qué _JS se comporta de la manera en que lo hace. en el bit de referencia del método: es bastante extraño que, cuando creo una referencia de función (no método, una función) en una función que se adjunta a un objeto, esa misma función ref se invoque como un método –

+0

+1 para el separar la sugerencia del método, por cierto –

+0

@EliasVanOotegem, se debe a que tan pronto como se incorpora esa función a un objeto, se convierte en un método de ese objeto. Es lo opuesto a lo que sucede cuando _detach_ un método. – Alnitak

Cuestiones relacionadas