2012-05-09 19 views
5

Ho pensato di avere una comprensione ragionevole dell'oggetto this in JavaScript. Quando ho a che fare con oggetti, callback, ed eventi e gestori, non ho avuto problemi da tempo immemorabile. Ora, tuttavia, tutto è cambiato.Chiusura JavaScript e questo oggetto

Mi sono innamorato perdutamente di JavaScript. Pure JS, cioè non jQuery, prototype.js, dojo ... Quindi, naturalmente, ho iniziato a utilizzare le chiusure. In alcuni casi, però, lo this mi sta prendendo alla sprovvista. Prendete questo frammento per una:

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

In tutti e tre i casi, i registri this come l'oggetto finestra. Si tratta di una soluzione semplice, naturalmente: opere

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

questa correzione, l'ho messo qui per evitare le persone che rispondono presente, senza spiegare che cosa ho bisogno di sapere: perché questo comporta il modo in cui lo fa qui?

assicura che il chiamante sia effettivamente l'oggetto a cui appartiene la chiusura. Quello che non riesco a capire è questo: Abbastanza sicuro, this punti per l'oggetto finestra nel primo caso, ma in altri casi, non dovrebbe. Ho provato a registrare this nella funzione makeClosure appena prima di tornare, e ha registrato l'oggetto stesso, non l'oggetto window. Ma quando viene utilizzata la chiusura effettiva, this torna a puntare all'oggetto finestra. Perché?

L'unica cosa che posso pensare è che, passando la funzione anyFunc come argomento, sto effettivamente passando window.anyFunc. Così ho provato questa soluzione rapida:

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

con i risultati attesi, this punti ora agli oggetti, ma ancora una volta: perché? Ho qualche idea (theFunc è un riferimento alla funzione nell'ambito locale [this > private: theFunc]?), Ma sono sicuro che ci sono persone qui con molto più know-how quando si tratta di JS, quindi speravo di ottenere qualche spiegazione o link ad articoli pena di leggere da loro ...

Grazie

Aggiornamento

Here's a fiddle, può essere ho lasciato fuori qualcosa, ma qui questo registra ogni sorta di cose;)

E dit/Update 2

The case that confuses me è qui.

Modifica finale

Ok, questo è sempre un messaggio piuttosto disordinato. Quindi, per chiarire: quello che mi aspettavo era un comportamento simile a questo:

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

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

Quello che mi ha colto, era che la funzione anyFunc non è stato dichiarato nell'ambito di applicazione corretta e, quindi, this indicò l'oggetto finestra. Ho trovato questo fuori leggendo un ancient scroll che ho trovato da qualche parte sul web.

Ma qualcosa un po 'più complicato è accaduto perché l'oggetto funzione ora denominato da globalVar stato creato con un [[scope]] immobili riferibile ad una catena portata contenente la/oggetto variabile attivazione appartenente al contesto di esecuzione in che è stato creato (e l'oggetto globale). Ora l'oggetto Activation/Variable non può essere garbage collector poiché l'esecuzione dell'oggetto function a cui fa riferimento globalVar dovrà aggiungere l'intera catena dell'ambito dalla sua proprietà [[scope]] all'ambito del contesto di esecuzione creato per ogni chiamata a esso.

Quindi quello che dovevo fare, è stato semplificare piuttosto che complicare le cose:

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

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

che dovrebbe funzionare, giusto?

PS: I'll tranne la risposta di Alnitak, ma un ringraziamento speciale va a Felix Kling per tutta la pazienza e informazioni.

+0

non si è davvero ottenere ciò che altra risposta che un link ad una spiegazione globale di chiusure potrebbe essere fatto. Questo corso è una lettura eccellente e dovrebbe rimuovere tutte le incertezze: http://ejohn.org/apps/learn/ –

+0

Il problema non è che non ottengo chiusure. Quello che sta causando il mal di testa è che non sono del tutto sicuro di cosa succede all'oggetto "questo" quando faccio le chiusure come faccio io qui –

+2

Non riesco a riprodurre il ricordo che descrivi. Ottengo 'DOMWindow',' Object', 'Object': http://jsfiddle.net/GErkX/, mentre la tua seconda" correzione "mi dà 3 x' DOMWindow': http://jsfiddle.net/tZXQF/ –

risposta

4

Non appena si chiama:

return func(par); 

Stai creando un nuovo ambito (con il proprio this) e in questo caso perché non è stato specificato un oggetto, di solito this === window o indefinita in modalità rigorosa. La funzione chiamata non ha e eredita tutto ciò che era this nell'ambito della chiamata.

modi per impostare un valore per questo sono:

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

o

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

ci sono anche:

+1

o -apparentemente- crea una var locale nella funzione 'makeClosure', e assegna il parametro func a quella variabile ... inoltre,' return func (par) 'è il corpo della funzione, di una funzione che viene restituita a un oggetto , quindi ho pensato che la chiamata a 'func (par)' sarebbe stata fatta nel contesto dell'oggetto. Perché non lo è? –

+0

@EliasVanOotegem Sto ancora cercando di capire come funziona il caso precedente ... Non riesco a replicarlo localmente. – Alnitak

+2

@EliasVanOotegem: Penso che i tuoi test siano difettosi in qualche modo, specialmente l'ultimo. Ogni volta che si effettua una semplice chiamata chiamata 'foo()', 'this' si riferirà all'oggetto globale, indipendentemente da dove è stata definita la funzione. In quale ambiente stai testando il codice? La creazione di una variabile locale non fa differenza in questo caso. Anche il parametro è locale. La tua prima correzione funziona bene qui: http://jsfiddle.net/GErkX/ –

1

Il valore di this dipende solo dal fatto che si chiama la funzione come metodo o come una funzione .

Se si chiama come metodo, this sarà l'oggetto che il metodo appartiene:

obj.myFunction(); 

Se si chiama come una funzione, this sarà oggetto window:

myFunction(); 

Si noti che anche se si è in un metodo che appartiene a un oggetto, è necessario chiamare altri metodi nell'oggetto utilizzando la sintassi del metodo, altrimenti verranno chiamati come funzioni:

this.myOtherFunction(); 

Se si inserisce un riferimento metodo in una variabile, si staccherà dall'oggetto, e sarà chiamato come una funzione:

var f = obj.myFunction; 
f(); 

I call e apply metodi sono utilizzati per chiamare una funzione come un metodo, anche se non è un metodo in oggetto (o se si tratta di un metodo in un oggetto diverso):

myFunction.call(obj); 
+0

grazie, ma non è davvero una risposta a questa domanda ... attualmente utilizzo 'call', ma voglio sapere _why_ JS si comporta come fa. sul bit di riferimento del metodo: è ben strano che, quando creo un riferimento di funzione (non un metodo, una funzione) in una funzione collegata a un oggetto, tale funzione verrà chiamata come metodo –

+0

+1 per il scollega il suggerimento del metodo, btw –

+0

@EliasVanOotegem perché non appena _attach_ si riferisce a un oggetto, diventa un metodo per quell'oggetto. È l'opposto di ciò che accade quando si esegue _detach_ un metodo. – Alnitak