Добавление кода в функцию javascript программно - PullRequest
105 голосов
/ 03 февраля 2012

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

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

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

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

Ответы [ 6 ]

207 голосов
/ 04 февраля 2012

Если 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 и передать любые аргументы, переданные в качестве отдельных аргументов, независимо от того, сколько их было.

30 голосов
/ 04 февраля 2012

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

var oldFunction = someFunction;

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

someFunction = function(){
  // do something before
  oldFunction();
  // do something after
};
10 голосов
/ 04 февраля 2012

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

var old_someFunction = someFunction;
someFunction = function(){
    alert('Hello');
    old_someFunction();
    alert('Goodbye');
}
6 голосов
/ 04 февраля 2012

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

var the_old_function = someFunction;
someFunction = function () {
    /* ..My new code... */
    the_old_function();
    /* ..More of my new code.. */
}
5 голосов
/ 19 июля 2015

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

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 | новая функция

0 голосов
/ 26 января 2019

Шаблон прокси (используемый 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);
...