2009-10-11 13 views
8

Diciamo che ottengo una funzione anonima che è necessario agire sul suo contesto, ma è diverso se è associato a "finestra" oa un oggetto sconosciuto.Come ottenere il "questo" (scope) di una funzione anonima Javascript?

Come ottenere un riferimento all'oggetto da cui viene chiamata una funzione anonima?

EDIT, del codice:

var ObjectFromOtherLibIAmNotSupposedToknowAbout = { 
    foo : function() { 
     // do something on "this" 
    } 
} 

var function bar(callback) { 
    // here I want to get a reference to 
    // ObjectFromOtherLibIAmNotSupposedToknowAbout 
    // if ObjectFromOtherLibIAmNotSupposedToknowAbout.foo is passed 
    // as callback 
} 

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo); 

Si potrebbe legittimamente chiedere, perché il diavolo ti piacerebbe fare qualcosa di simile. Beh, per prima cosa ho voluto disfare gli argomenti passati come array. Proprio come il Python operatore "*" fa:

>>> args = [1,2,3] 
>>> def foo(a,b,c) : 
     print a,b,c 
>>> foo(*args) 
1 2 3 

ho scavato nel SO e trovato un post dice di usare "apply()":

function bar(callback, args){ 
    this[callback].apply(this, args); 
} 

interessante dal momento che è intenzione di utilizzare l'attuale " questo "se in un oggetto, e" finestra "se non.

ma penso che ci sia un problema:

se "bar()" è di per sé in un oggetto, quindi "questo" si riferiscono alla "barra()" contenitore, Perciò non funzionerà.

BTW, Mi piacerebbe non passare l'oscilloscopio come parametro.

Posso ovviamente concatenare gli argomenti e la funzione come stringa, quindi utilizzare eval, ma mi piacerebbe usarlo solo se non riesco a trovare qualcosa di più pulito.

Naturalmente, se è solo impossibile (dopo tutto, potrebbe essere), allora farò:

function foo(func, args) 
{ 
    eval("func("+args.join(", ")+");"); 
} 

EDIT 2: pieno scenario, come ha chiesto nei commenti.

Sto usando qunit per eseguire test di unità in Javascript. È bello, ma mi manca un modo per verificare se qualcosa solleva un'eccezione.

Il test più semplice è fatto in questo modo:

/** 
* Asserts true. 
* @example ok($("a").size() > 5, "There must be at least 5 anchors"); 
*/ 
function ok(a, msg) { 
    _config.Test.push([ !!a, msg ]); 
} 

L'idea è di fare qualcosa di simile:

jqUnit.prototype.error = function(func, args, msg) 
{ 
    try 
    { 
     eval("func("+args.join(", ")+");"); 
     config.Test.push([ false, msg + " expected : this call should have raised an Exception" ]); 
    } catch(ex) 
    { 
     _config.Test.push([ true, msg ]); 
    } 
}; 

Se ho potuto sbarazzarsi di eval, sarebbe bello. E perché non voglio usare l'oscilloscopio come parametro? Perché potresti voler fare un ciclo su un contenitore facendo riferimento a 20 funzioni con diversi ambiti e testarli tutti nel ciclo invece di scrivere a mano le cose.

+2

Potrebbe fornire qualche esempio di codice? Ho qualche difficoltà a capire cosa vuoi veramente. –

+0

Sì, non sono sicuro di cosa stiate cercando. Si prega di fornire un esempio di codice. – SolutionYogi

+0

Hai ragione, fatto. –

risposta

6

e-satis,

L'unico modo per farlo è quello di utilizzare chiamata o metodo per impostare corrette 'contesto' applicare.

Per risolvere il problema, modificare la funzione della barra per accettare la funzione di callback e l'ambito da applicare a tale funzione di callback.

function bar(callback, scope) 
{ 
    callback.apply(scope); 
} 

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo, ObjectFromOtherLibIAmNotSupposedToknowAbout); 

In alternativa, è possibile utilizzare un metodo "bind".

Function.prototype.bind = function(context) { 
    var fun = this; 
    return function(){ 
    return fun.apply(context, arguments); 
    }; 
}; 

Ora, è possibile lasciare la vostra funzione bar intatta e modificare il codice chiamante a guardare come,

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo.bind(ObjectFromOtherLibIAmNotSupposedToknowAbout)); 

EDIT:

Come ho sottolineato nel commento, è responsabilità di il codice chiamante per passare una funzione di callback corretta. La tua funzione 'bar' non può determinare quale ambito usare, punto.

Prendete questo per un esempio,

var LibObject = { foo: function() { //use this inside method } }; 

var fooFunction = LibOjbect.foo; 
bar(fooFunction); 

Come hai intenzione di capire quello che dovrebbe essere la portata? Non c'è niente per te per 'analizzare' ora e non c'è modo di modificare la funzione 'barra' per fare in modo che funzioni.

+1

Ciao, grazie, ma è per l'hacking di una piccola estensione per un framework di unit test, non ho l'ambito della funzione testata a portata di mano (e non dovrebbe avere). –

+0

Sei incaricato di scrivere quella chiamata al metodo della barra o no? In JS, le funzioni non sono collegate agli oggetti e il motore JS deciderà quale contesto utilizzare, a meno che non lo specifichi esplicitamente usando call/apply. – SolutionYogi

+0

Lo sono, ma non posso chiedere che 1000 test vengano scritti in un modo (sintassi qunit), una certa firma e faccia eccezione per un piccolo trucco che ho codificato per comodità. Romperebbe la coerenza dell'API, non ne vale la pena. Nessun problema, questa domanda non riguarda la vita o la morte :-) –

Problemi correlati