2013-05-05 13 views
8

Ho questo semplice codice:Perché perdo il contesto di questo in Javascript?

var o = { 
    a: 1, 
    b: 2, 
    f1: function() 
    { 
     alert(this.b); 
    } 
} 

var o2 = { 
    a: 11, 
    b: 22, 
    f2: function (j) 
    { 
     j(); 
    } 
} 

Ma l'esecuzione di questo codice:

o2.f2(o.f1) cede indefinita. (mentre mi aspetto "22" come risultato)

Ora, so che il contesto è andato da qualche parte. e quindi Se cambio il codice in o2 a:

f2: function (j) 
    { 
     j.apply(this); 
    } 

Lo fa, ovviamente, il lavoro.

Ma la mia domanda è:

  • In quale fase ho perso il contesto?

non capisco: quando j() è in esecuzione, non v'è una proprietà b nell'oggetto o2.

Cosa mi manca?

jsbin

+9

Quando lo chiami come 'f()' - I metodi JavaScript sono * "funzioni non associate" * (cioè, a differenza dei metodi in altre lingue sono * non * associati a un particolare oggetto/istanza) ed è la chiamata -site che determina il 'this' quando viene invocato. (Naturalmente, vedi ['Function.bind'] (https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind) o emulazioni equivalenti.) – user2246674

+0

Lo perdi al più presto mentre passi intorno a 'o.f1'. Se si esegue 'var x = o.f1' e si chiama' x() ',' f1' non è più legato. – Blender

+0

@Blender Lo so. ma perché "questo" non riguarda 'o2'? questo è ciò che non capisco –

risposta

6

Ho trovato che Crockford aveva una descrizione eccellente del modo in cui funziona.Le funzioni in JavaScript possono essere invocati in 4 stili:

  1. La "funzione" stile
  2. Il "metodo" stile
  3. Lo stile "Costruttore"
  4. Lo stile "chiamata o applicare".

Potrei avere i nomi esatti sbagliati lì, ma lo spirito è lo stesso. Dovresti assolutamente ottenere il libro "JavaScript: The Good Parts" se non lo hai.

Quindi, sempre alla tua domanda. La cosa fondamentale è che il valore se "questo" dipende dallo stile che usi.

// function invocation style, 
var f = function() { console.debug(this); } 
f(); // "this" is bound to the global object. 

// "method" invocation style 
var obj = { 
    f: function() { console.debug(this); } 
}; 

obj.f(); // "this" is bound to "obj", the object on which the function was invoked 

// so important bit is : 

var f = obj.f; 
f(); // "this" is global object 
obj.f() // "this" is obj 

Nel tuo esempio si stanno perdendo "questo" a causa del modo in cui si invoca la funzione.

1

Se lo fate come i seguenti,

funzione verrà chiamata nel contesto o2

var o2 = { 
    a: 11, 
    b: 22, 
    f2: function (j){ 
     this.temp = j; 
     this.temp(); 
    } 
}; 

anche questi funzionerà anche:

f2: function (j){ 
     j.apply(this); 
} 

f2: function (j){ 
     j.apply(o2); 
} 

Altrimenti lo chiami solo lik e una funzione ordinaria fuori dal contesto.

j Viene estratto dal suo contesto e non è stata eseguita alcuna chiusura complessa (che non è il tuo scopo), quindi per far funzionare "questo" in esso è necessario un ambito. Questo ambito nella tua domanda per j è window, che non ha "b" in esso quindi ottieni un "indefinito".

+0

bel trucco .. + 1. –

+0

sì, l'uso di "questo" è una risposta – Cherniv

0

controllare questo test:

o.f1(); // alerts 2 

var f3 = o.f1; // (*) 

f3(); // alerts undefined 

o2.f2(f3); // alerts undefined 

f3.apply(o2); // alerts 22 

mi rendo conto che quando si passa una funzione come parametro di contesto si perde esattamente nello stesso modo in cui è perso nel (*) indicate nel codice di cui sopra.

Che cosa succede è j = arguments[0] = o.f1 ea questo punto si perde il contesto.

Quando passa una funzione come parametro, si passa semplicemente il riferimento in memoria a quella funzione. Senza vincolare il contesto, fallirai nella semplice chiamata j(). Ecco perché è necessario utilizzare apply o il trucco this mostrato da Ihsan.

Problemi correlati