Програмне додавання коду до функції javascript


114

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

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

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

Я не зовсім впевнений, що це можливо, але я подумав, що перевірю.


3
перша відповідь на це має допомогти вам
DarkAjax

Ви хочете щось подібне до функції зворотного дзвінка?
sinemetu1

Відповіді:


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та передавати будь-які аргументи, передані як окремі аргументи, незалежно від того, скільки їх було.


10
Ця відповідь справді повинна бути першою - вона зберігає функціональність відповідної функції ... +1 від мене.
Рейд

17
+1 за використання apply: це єдина відповідь, яка реально вирішує проблему.
самотній день

2
@minitech: Що ... Ви не знайомі з funcitonключовим словом JavaScript ? ;) Дякую за редагування

1
@gdoron: Я щойно додав коментар. Якщо я просто просто не переплутаний зараз, я не знаю жодного браузера, який не сприйме фактичний аргумент як другий аргумент .apply(). Але якби наприклад це був об’єкт, схожий на масив, подібний до об’єкта jQuery, то так, деякі браузери

2
Ця відповідь, як це відбувається, вирішує проблему використання вбудованих об'єктів для управління окремими відео YouTube, вбудованими через API Iframe YouTube. Ви не знаєте, як довго я намагаюся обійти той факт, що API вимагає, щоб зворотний виклик був єдиною глобальною функцією, коли я намагаюся створити клас для обробки даних кожного відео унікальним модульним способом. Ти мій герой на день.
Карнікс

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.