Добавление кода в функцию javascript программным способом

114

Я пытаюсь настроить существующую библиотеку JS, не изменяя исходный код JS. Этот код загружается в несколько внешних JS-файлов, к которым у меня есть доступ, и я хотел бы изменить одну из функций, содержащихся в исходном файле, без копирования и вставки всего этого во второй JS-файл.
Так, например, закрытый JS может иметь такую ​​функцию:

var someFunction = function(){
    alert("done");
}

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

Я не совсем уверен, что это возможно, но решил проверить.

Munzilla
источник
3
первый ответ на этот вопрос должен вам помочь
DarkAjax
Вы хотите что-то вроде функции обратного вызова?
sinemetu1 03

Ответы:

220

Если someFunctionона доступна глобально, вы можете кэшировать функцию, создать свою собственную и вызвать ее.

Так что если это оригинал ...

someFunction = function() {
    alert("done");
}

Ты бы сделал это ...

someFunction = (function() {
    var cached_function = someFunction;

    return function() {
        // your code

        var result = cached_function.apply(this, arguments); // use .apply() to call it

        // more of your code

        return result;
    };
})();

Вот скрипка


Обратите внимание, что я использую .applyдля вызова кешированной функции. Это позволяет мне сохранять ожидаемое значение thisи передавать любые аргументы, которые были переданы как отдельные аргументы, независимо от того, сколько их было.

user1106925
источник
10
Этот ответ действительно должен быть верхним - он сохраняет функциональность рассматриваемой функции ... +1 от меня.
Reid
17
+1 за использование apply: это единственный ответ, который действительно решает проблему.
lonesomeday
2
@minitech: Что ... Вы не знакомы с funcitonключевым словом JavaScript ? ;) Спасибо за правку
1
@gdoron: Я только что добавил комментарий. Если я не совсем запутался прямо сейчас, я не знаю ни одного браузера, который не принял бы фактический объект Arguments в качестве второго аргумента для .apply(). Но если бы это был объект, подобный массиву, например объект jQuery, то да, некоторые браузеры
2
Этот ответ, как оказалось, решает проблему использования экземпляров объектов для управления отдельными видеороликами YouTube, встроенными через YouTube Iframe API. Вы не представляете, как долго я пытался обойти тот факт, что API требует, чтобы обратный вызов был единой глобальной функцией, когда я пытаюсь создать класс для обработки данных каждого видео уникальным модульным способом. Ты мой герой дня.
Carnix
31

сначала сохраните фактическую функцию в переменной ..

var oldFunction = someFunction;

затем определите свой собственный:

someFunction = function(){
  // do something before
  oldFunction();
  // do something after
};
клубок
источник
9
Если ваша функция является методом, вам нужно будет использовать ее applyдля ее вызова. См. Ответ.
hugomg
Он работал для меня, но нужно заменить someFunctionс window.someFunctionв коде выше. Причина в том, что моя функция была объявлена ​​внутри $(document).ready()обработчика jquery .
Нельсон
10

Вы можете создать функцию, которая вызывает ваш код, а затем вызывает функцию.

var old_someFunction = someFunction;
someFunction = function(){
    alert('Hello');
    old_someFunction();
    alert('Goodbye');
}
Ракетный Хазмат
источник
6

Я не знаю, можете ли вы обновить функцию, но в зависимости от того, как на нее ссылаются, вы можете создать новую функцию вместо нее:

var the_old_function = someFunction;
someFunction = function () {
    /* ..My new code... */
    the_old_function();
    /* ..More of my new code.. */
}
Нед Батчелдер
источник
5

Также. Если вы хотите изменить локальный контекст, вам нужно воссоздать функцию. Например:

var t = function() {
    var a = 1;
};

var z = function() {
    console.log(a);
};

Сейчас

z() // => log: undefined

затем

var ts = t.toString(),
    zs = z.toString();

ts = ts.slice(ts.indexOf("{") + 1, ts.lastIndexOf("}"));
zs = zs.slice(zs.indexOf("{") + 1, zs.lastIndexOf("}"));

var z = new Function(ts + "\n" + zs);

И

z() // => log: 1

Но это самый простой пример. Для обработки аргументов, комментариев и возвращаемого значения потребуется еще много работы. К тому же есть еще много подводных камней.
toString | ломтик | indexOf | lastIndexOf | новая функция

Иван Черный
источник
1

Шаблон прокси (используемый пользователем user1106925) можно поместить внутри функции. Тот, который я написал ниже, работает с функциями, которые не входят в глобальную область видимости, и даже с прототипами. Вы бы использовали это так:

extender(
  objectContainingFunction,
  nameOfFunctionToExtend,
  parameterlessFunctionOfCodeToPrepend,
  parameterlessFunctionOfCodeToAppend
)

Во фрагменте ниже вы можете увидеть, как я использую функцию для расширения test.prototype.doIt ().

// allows you to prepend or append code to an existing function
function extender (container, funcName, prepend, append) {

    (function() {

        let proxied = container[funcName];

        container[funcName] = function() {
            if (prepend) prepend.apply( this );
            let result = proxied.apply( this, arguments );
            if (append) append.apply( this );
            return result;
        };

    })();

}

// class we're going to want to test our extender on
class test {
    constructor() {
        this.x = 'instance val';
    }
    doIt (message) {
        console.log(`logged: ${message}`);
        return `returned: ${message}`;
    }
}

// extends test.prototype.doIt()
// (you could also just extend the instance below if desired)
extender(
    test.prototype, 
    'doIt', 
    function () { console.log(`prepended: ${this.x}`) },
    function () { console.log(`appended: ${this.x}`) }
);

// See if the prepended and appended code runs
let tval = new test().doIt('log this');
console.log(tval);

Pwilcox
источник