Добавить код в конец существующей функции

Мне нужно вызвать функцию bar(), когда срабатывает функция foo(). У меня нет контроля над функцией foo или она изменится в будущем. У меня такая ситуация регулярно (и я ее ненавижу).

Я написал эту функцию, чтобы добавить мой код в конец функции foo:

function appendToFunction(fn,code){ 
if(typeof fn == 'function'){
    var fnCode = fn.toString() ;
    fnCode = fnCode.replace(/\}$/,code+"\n}") ;
    window.eval(fnCode);                    // Global scope
    return true ;
}
else{
    return false ;
}
}

например:

appendToFunction(foo,"bar();");

Это поражает меня как ужасную идею, но она работает. Может ли кто-нибудь указать мне лучшее (безопасное) направление, пожалуйста.

EDIT: foo - это не определенная функция, а множество функций, с которыми я сталкиваюсь. Они не изменяются динамически на странице. Но они могут время от времени меняться (например, требования к проверке формы).

Решение: Я остановился на модифицированной версии Adam Answer. Это не идеально, но это лучше, чем у меня:

var oldFoo = foo ;
foo = function(){
        var result = oldFoo.apply(this, arguments);
        bar();
        return result ;
}

NB. Следите за некоторыми встроенными функциями в IE6/7, которые не имеют метода .apply().

Ответы

Ответ 1

Вы можете просто переопределить foo с помощью специальной функции, которая вызывает оригинал.

например.

var old_foo = foo;
foo = function() {
  old_foo();
  bar();
}

Вы также должны передать любые аргументы, которые foo берет на себя через вашу заменяющую функцию.

Ответ 2

function extend(fn,code){
  return function(){
    fn.apply(fn,arguments)
    code.apply(fn,argumnets)
  }
}

и используйте его следующим образом:

function appendToFunction(fn,code){ 
    if(typeof fn == 'function'){
        var fnCode = fn.toString() ;
        fnCode = fnCode.replace(/\}$/,code+"\n}") ;
        window.eval(fnCode);                    // Global scope
        return true ;
    }
    else{
        return false ;
    }
}

appendToFunction = extend(appendToFunction,function(){/*some code*/});

это даст вам тот же "this" в обеих функциях

Ответ 3

Вы можете сделать что-то вроде этого: ДЕМО.

function foo() {
 console.log('foo');
}

function appendToFunction(fn, callback) {
  window[fn] = (function(fn){
    return function() {
      fn();
      callback();
    }
 }(window[fn]));
}

appendToFunction('foo', function(){console.log('appended!')});

foo();

Ответ 4

Хм, это касается и меня, вы упомянули, что

У меня такая ситуация регулярно (и я ее ненавижу).

Не возражаете, если я спрошу, в каком сценарии это происходит? Является ли он в корпоративном масштабе или в личном объеме проекта? У вас явно есть голова на плечах и знаете, что то, что вы делаете, необычно, поэтому мне интересно, есть ли альтернативное решение.

Я спрашиваю: этот подход потенциально может открыть проблему проблем. Что делать, если foo не работает, например, или если foo возвращает значение средней оценки? Простое добавление bar к фактической функции не гарантирует ее выполнения. Предварительно ожидающая его, с другой стороны, означает, что она скорее всего будет выполнена, но, по-моему, это не очень хороший подход.

Рассматривали ли вы пересмотр функции foo? Я знаю, что это может показаться глупым вопросом, но это может стоить того, если вы столкнетесь с подобными проблемами. Если вы хотите сохранить абстрагирование объектов, вы можете применить подход "обработчик событий", в результате чего foo запускает событие на window, которое, в свою очередь, запускает bar, будет ли это работать в вашем случае.

В качестве альтернативы, если вы знаете, что такое foo, и что он делает, вы можете подключить к нему прототип, если это объект, а затем соответствующим образом изменить код. Однако вы отметили, что эта функция открыта для изменения, что может сделать эту опцию излишней, но тем не менее это возможное решение.

Ответ 5

Вы можете добавить или добавить новый код к существующей функции, просто слияние, используя, например:

function mergeFunctions(function1, function2, instance1, instance2, numberOfArgumentsToPassToFunc1) {
    return function() {
        var _arguments  = Array.prototype.slice.apply(arguments);
        var _arguments1 = _arguments.slice(0, numberOfArgumentsToPassToFunc1);
        var _arguments2 = _arguments.slice(numberOfArgumentsToPassToFunc1);
        var that = this;
        (function(function1, function2) {
            if (typeof function1 == "function") {
                if (typeof instance1 != "undefined") {
                    function1.apply(instance1, _arguments1);
                }
                else if (that == window) {
                    function1.apply(function1, _arguments1);
                }
                else {
                    var compare = mergeFunctions(function(){}, function(){});
                    if (that.toString() == compare.toString()) {
                        function1.apply(function1, _arguments1);
                    }
                    else {
                        function1.apply(that, _arguments1);
                    }
                }
            }
            if (typeof function2 == "function") {
                if (typeof instance2 != "undefined") {
                    function2.apply(instance2, _arguments2);
                }
                else if (that == window) {
                    function2.apply(function2, _arguments2);
                }
                else {
                    var compare = mergeFunctions(function(){}, function(){});
                    if (that.toString() == compare.toString()) {
                        function2.apply(function2, _arguments2);
                    }
                    else {
                        function2.apply(that, _arguments2);
                    }
                }
            }
        })(function1, function2);
    }
}



базовый пример:

// Original function:
var someFunction = function(){
    console.log("original content");
};

// Prepend new code:
// --------------------------------------------------------
someFunction = mergeFunctions(function() {
    console.log("--- prepended code");
}, someFunction);

// Testing:
someFunction();

// Outout:
// [Log] --- prepended code
// [Log] original content


// Append new code:
// --------------------------------------------------------
someFunction = mergeFunctions(someFunction, function() {
    console.log("appended code");
});

// Testing:
someFunction();

// Output:
// [Log] --- prepended code
// [Log] original content
// [Log] appended code



Примечание, что функция слияния пытается применить ожидаемый 'this' к объединенным частям, иначе вы можете просто передать желаемый 'this' им, а также обработать относительные аргументы. < ш > более общий пример может быть следующим:

function firstPart(a, b) {
    console.log("--- first part");
    console.log("'this' here is:");
    console.log(this.name);
    console.log("a: "+a);
    console.log("b: "+b);
}

function MyObject() {
    this.x = "x property of MyObject";
}

MyObject.prototype.secondPart = function (y) {
    console.log("");
    console.log("--- second part");
    console.log("'this' here is:");
    console.log(this.name);
    this.x = y;
    console.log("x: "+this.x);
}

MyObject.prototype.merged = mergeFunctions(firstPart, MyObject.prototype.secondPart, firstPart, MyObject, 2);

// Testing
var test = new MyObject();
test.merged("a parameter", "b parameter", "x parameter overrides x property of MyObject");

// Console output:
// [Log] --- first part
// [Log] 'this' here is:
// [Log] firstPart
// [Log] a: a parameter
// [Log] b: b parameter
// [Log] 
// [Log] --- second part
// [Log] 'this' here is:
// [Log] MyObject
// [Log] x: x parameter overrides x property of MyObject