2008-11-17 17 views
147

Tengo una función, a(), que deseo anular, pero también debo hacer que el a() original se realice en un orden según el contexto. Por ejemplo, a veces cuando estoy generando una página que querrá para anular así:Reemplazando una función de JavaScript al hacer referencia al original

function a() { 
    new_code(); 
    original_a(); 
} 

y en ocasiones como esta:

function a() { 
    original_a(); 
    other_new_code(); 
} 

¿Cómo consigo que original_a() desde dentro de la excesiva montando a()? ¿Es posible?

Por favor, no sugiera alternativas a la superación de esta manera, sé de muchos. Estoy preguntando acerca de esta manera específicamente.

Respuesta

172

se podría hacer algo como esto:

var a = (function() { 
    var original_a = a; 

    if (condition) { 
     return function() { 
      new_code(); 
      original_a(); 
     } 
    } else { 
     return function() { 
      original_a(); 
      other_new_code(); 
     } 
    } 
})(); 

Declarar original_a dentro de una función anónima lo mantiene de estorbar el espacio de nombres global, pero que está disponible en las funciones internas.

Como Nerdmaster mencionado en los comentarios, asegúrese de incluir el () al final. Desea llamar a la función externa y almacenar el resultado (una de las dos funciones internas) en a, no almacenar la función externa en a.

+0

¡Gracias! Eso es muy útil. – Kev

+1

wow. ultra cool – naveen

+21

Para cualquier idiota por ahí como yo - preste mucha atención a la "()" al final - sin eso, devuelve la función externa, no las funciones internas :) – Nerdmaster

75

El Proxy pattern pueden ayudar:

(function() { 
    // log all calls to setArray 
    var proxied = jQuery.fn.setArray; 
    jQuery.fn.setArray = function() { 
     console.log(this, arguments); 
     return proxied.apply(this, arguments); 
    }; 
})(); 

Lo anterior envuelve su código en una función para ocultar el "proxy" -Variable. Guarda el método setArray de jQuery en un cierre y lo sobrescribe. El proxy registra todas las llamadas al método y delega la llamada al original. El uso de apply (this, arguments) garantiza que el que llama no podrá notar la diferencia entre el método original y el proxy.

+7

de hecho. el uso de 'aplicar' y 'argumentos' hace esto mucho más sólido que las otras respuestas –

+3

No tiene idea de cuánto aprendí de ese enlace.Gracias señor –

+0

Funcionó como un encanto. Muchas gracias. – kevin11

26

Gracias a todos, el patrón de proxy realmente ayudó ..... En realidad, quería llamar a una función global foo .. En ciertas páginas necesito hacer algunos controles. Entonces hice lo siguiente.

//Saving the original func 
var org_foo = window.foo; 

//Assigning proxy fucnc 
window.foo = function(args){ 
    //Performing checks 
    if(checkCondition(args)){ 
     //Calling original funcs 
     org_foo(args); 
    } 
}; 

Thnx esto realmente me ayudó a salir

+1

Al seguir el [patrón de proxy] (http://api.jquery.com/Types/#Proxy_Pattern), será importante en algunos casos implementar este detalle que no está presente en el código de esta Respuesta: En lugar de 'org_foo (args) ', llame a' org_foo.call (this, args) '. Eso mantiene 'this' como lo hubiera sido cuando window.foo llama normalmente (no proxied). Ver [esta respuesta] (http://stackoverflow.com/a/915618/1357094) – cellepo

8

Puede anular una función usando una construcción como:

function override(f, g) { 
    return function() { 
     return g(f); 
    }: 
} 

Por ejemplo:

a = override(a, function(original_a) { 
     if (condition) { new_code(); original_a(); } 
     else { original_a(); other_new_code(); } 
}); 
+0

+1 ¡Esto es mucho más legible que los otros ejemplos de patrones de proxy! –

+1

... pero si no me equivoco, esto se limita a las funciones de argumento cero, o al menos a un número predeterminado de argumentos. ¿Hay alguna manera de generalizarlo a números arbitrarios de argumentos sin perder la legibilidad? –

+0

@MuMind: sí, pero utilizando correctamente el patrón de proxy - vea [mi respuesta] (http://stackoverflow.com/a/296713/1269037). –

4

el paso de argumentos arbitrarios:

a = override(a, function(original_a) { 
    if (condition) { new_code(); original_a.apply(this, arguments) ; } 
    else { original_a.apply(this, arguments); other_new_code(); } 
}); 
+1

¿no se refieren los argumentos a 'original_a' aunque? –

1

Los ejemplos anteriores no aplican correctamente this o pasan arguments correctamente a la anulación de la función. Underscore _.wrap() ajusta las funciones existentes, aplica this y pasa arguments correctamente.Ver: http://underscorejs.org/#wrap

0

Tenía un código escrito por otra persona y quería agregar una línea a una función que no pude encontrar en el código. Entonces, como solución, quería anularla.

Ninguna de las soluciones funcionó para mí.

Aquí es lo que funcionó en mi caso:

if (typeof originalFunction === "undefined") { 
    originalFunction = targetFunction; 
    targetFunction = function(x, y) { 
     //Your code 
     originalFunction(a, b); 
     //Your Code 
    }; 
} 
0

He creado un pequeño ayudante para un escenario similar, porque a menudo necesitaba para anular las funciones de varias bibliotecas. Este ayudante acepta un "espacio de nombres" (el contenedor de funciones), el nombre de la función y la función de anulación. Reemplazará la función original en el espacio de nombres referido con el nuevo.

La nueva función acepta la función original como primer argumento, y los argumentos de las funciones originales como el resto. Preservará el contexto cada vez. También admite funciones nulas y no nulas.

function overrideFunction(namespace, baseFuncName, func) { 
    var originalFn = namespace[baseFuncName]; 
    namespace[baseFuncName] = function() { 
     return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0))); 
    }; 
} 

Uso por ejemplo, con Bootstrap:

overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) { 
    // ... do stuff before base call 
    baseFn(obj); 
    // ... do stuff after base call 
}); 

que no creó ningún pruebas de rendimiento sin embargo. Posiblemente puede agregar algunos gastos indirectos no deseados que pueden o no ser un gran problema, dependiendo de los escenarios.

0

En mi opinión, las respuestas principales no son legibles/mantenibles, y las otras respuestas no vinculan adecuadamente el contexto. Aquí hay una solución legible que usa la sintaxis ES6 para resolver ambos problemas.

const orginial = someObject.foo; 
someObject.foo = function() { 
    if (condition) orginial.bind(this)(...arguments); 
}; 
+0

Pero el uso de la sintaxis de ES6 hace que su uso sea bastante restringido. ¿Tienes una versión que funciona en ES5? – eitch

0

La respuesta que ofrece @Matthew Crumley está haciendo uso de las expresiones de función invocados inmediatamente, para cerrar la 'a' de funcionalidad anterior, en el contexto de ejecución de la función devuelta. Creo que esta fue la mejor respuesta, pero personalmente, preferiría pasar la función 'a' como argumento para IIFE. Creo que es más comprensible.

var a = (function(original_a) { 
     if (condition) { 
      return function() { 
       new_code(); 
       original_a(); 
      } 
     } else { 
      return function() { 
       original_a(); 
       other_new_code(); 
      } 
     } 
    })(a); 
Cuestiones relacionadas