2009-10-11 11 views
8

Digamos que obtengo una función anónima y la necesidad de actuar en su contexto, pero es diferente si está vinculada a "ventana" o un objeto desconocido.¿Cómo obtener el "este" (alcance) de una función anónima de Javascript?

¿Cómo obtengo una referencia al objeto desde el que se llama a una función anónima?

EDITAR, algo de código:

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

Usted puede preguntar legítimamente, ¿por qué diablos le gustaría hacer algo así. Bueno, primero quería descomprimir los argumentos pasados ​​como una matriz. Al igual que el pitón "*" operador hace:

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

Cavé en el SO y halló una post diciendo a utilizar "apply()":

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

interesante ya que va a utilizar la corriente " esto "si en un objeto, y" ventana "si no.

pero creo que hay un problema:

si "barra()" es en sí misma en un objeto, a continuación, "esto" se refiere a la "barra()" contenedor, para ello no va a funcionar.

BTW, Me gustaría no pasar el alcance como un parámetro.

Por supuesto, puedo concatenar los argumentos y la función como una cadena y luego usar eval, pero me gustaría usar esto solo si no puedo encontrar algo más limpio.

Por supuesto, si sólo es imposible (después de todo, podría ser), entonces lo haré:

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

EDIT 2: escenario lleno, como se pidió en los comentarios.

Estoy usando qunit para realizar pruebas unitarias en Javascript. Es genial, pero echo de menos una forma de comprobar si algo plantea una excepción.

La prueba más básica se lleva a cabo de esa manera:

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

La idea es hacer algo como:

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

Si pudiera deshacerme de eval, que sería genial. ¿Y por qué no quiero usar el alcance como parámetro? Debido a que es posible que desee hacer un bucle en un contenedor haciendo referencia a 20 funciones con diferentes ámbitos y probarlos todos en el bucle en lugar de escribir las cosas a mano.

+2

¿Podría proporcionar algún ejemplo de código? Tengo algunas dificultades para entender lo que realmente quieres. –

+0

Sí, no estoy seguro de lo que está buscando. Proporcione alguna muestra de código. – SolutionYogi

+0

Tienes razón, listo. –

Respuesta

6

e-satis,

La única manera de hacerlo es utilizar la llamada o aplicar el método para establecer el 'contexto' correcto.

Para resolver su problema, modifique la función de su barra para aceptar la función de devolución de llamada, así como el alcance para aplicar a esa función de devolución de llamada.

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

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo, ObjectFromOtherLibIAmNotSupposedToknowAbout); 

Alternativamente, puede utilizar un método 'bind'.

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

Ahora, usted puede dejar su función de la barra sin tocar y modificar el código de llamada para que parezca,

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

EDIT:

Como he señalado en el comentario, es responsabilidad del el código de llamada para pasar una función de devolución de llamada correcta. Su función de 'barra' no puede determinar qué alcance usar, punto.

tomar esto como un ejemplo,

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

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

¿Cómo vas a averiguar cuál debe ser el alcance? No hay nada que pueda "analizar" ahora y no hay forma de que pueda modificar su función de "barra" para que esto funcione.

+1

Hola, gracias, pero es por hackear una pequeña extensión para un marco de prueba de unidad, no tengo el alcance de la función probada a mano (y no debería tener). –

+0

¿Está a cargo de escribir ese método de barra o no? En JS, las funciones no se adjuntan a los objetos y el motor JS decidirá qué contexto usar, a menos que lo especifique explícitamente usando call/apply. – SolutionYogi

+0

Lo soy, pero no puedo pedir que se escriban 1000 pruebas de una manera (sintaxis qunit), una firma determinada, y hacer una excepción para un pequeño truco que codifiqué para mi conveniencia. Rompería la consistencia API, no lo vale. No es gran cosa, esta pregunta no es una cuestión de vida o muerte :-) –

Cuestiones relacionadas