Хуки функции JavaScript - PullRequest
       15

Хуки функции JavaScript

12 голосов
/ 26 апреля 2009

РЕДАКТИРОВАТЬ: ОК, я считаю, что следующие решения действительны:

  1. Используйте плагин jQuery AOP. Он в основном оборачивает старую функцию вместе с ловушкой в ​​сэндвич функции и переназначает ее на имя старой функции. Это вызывает вложение функций с каждым новым добавленным хуком.
    Если jQuery для вас не пригоден, просто разграбьте исходный код, в плагине не было никаких зависимостей jQuery, а исходный код прост и очень мал.

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

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


Привет

Можно ли создать механизм, в котором функция A может иметь набор хуков (функций, которые будут выполняться до / после функции A)?

В идеале, функция A не должна знать о функции перехвата, поэтому мне не нужно изменять исходный код функции A для вызова перехватчиков. Что-то вроде:

A = function(){
    alert("I'm a naive function");
};
B = function(){
    alert("I'm having a piggyback ride on function A!"+
          "And the fool doesn't even know it!");
};
addHook(B, A)//add hook B to function A
A()
//getting alerts "I'm a naive function"/"I'm having a 
//piggyback ride on function A! And the fool doesn't even know it!"

Я пару часов пытался что-то взломать, но пока не повезло.

Ответы [ 7 ]

12 голосов
/ 26 апреля 2009

Может быть не красиво, но, похоже, работает ...

<script>

function A(x) { alert(x); return x; }
function B() { alert(123); }

function addHook(functionB, functionA, parent)
{
    if (typeof parent == 'undefined')
        parent = window;

    for (var i in parent)
    {
        if (parent[i] === functionA)
        {
            parent[i] = function()
            {
                functionB();
                return functionA.apply(this, arguments)
            }

            break;
        }
    }
}

addHook(B, A);

A(2);

</script>
10 голосов
/ 26 апреля 2009

Взгляните на плагин jQuery's AOP . В общем, Google "Javascript аспектно-ориентированного программирования".

3 голосов
/ 18 октября 2010

Очень простой ответ:

function someFunction() { alert("Bar!") }
var placeholder=someFunction;
someFunction=function() {
  alert("Foo?");
  placeholder();
}
2 голосов
/ 26 апреля 2009

Этот ответ не является окончательным, а скорее демонстрирует другую технику, чем предложенная до сих пор. Это использует тот факт, что функция в Javascript является объектом первого класса, и, как таковой, а) вы можете передать его как значение другой функции и б) вы можете добавить свойства к нему. Объедините эти черты со встроенными в функцию методами «вызова» (или «применения»), и вы сами приступите к решению.

var function_itself = function() {
    alert('in function itself');
}
function_itself.PRE_PROCESS = function() {
    alert('in pre_process');
}
function_itself.POST_PROCESS = function() {
    alert('in post_process');
}

var function_processor = function(func) {
    if (func.PRE_PROCESS) {
        func.PRE_PROCESS.call();
    }
    func.call();
    if (func.POST_PROCESS) {
        func.POST_PROCESS.call();
    }        
}
1 голос
/ 27 сентября 2011

Следующая функция предоставит вам до и после хуки, которые можно сложить. Таким образом, если у вас есть ряд потенциальных функций, которые необходимо запустить до или после данной функции, это будет рабочее решение. Это решение не требует jQuery и использует собственные методы массива (не требуются прокладки). Он также должен быть контекстно-зависимым, поэтому, если вы вызываете исходную функцию с контекстом, она должна выполняться каждый до и после функции аналогичным образом.

// usage: 
/*
function test(x) {
    alert(x);
}

var htest = hookable(test);

htest.addHook("before", function (x) {
    alert("Before " + x);
})
htest.addHook("after", function (x) {
    alert("After " + x);
})

htest("test") // => Before test ... test ... After test
*/
function hookable(fn) {
    var ifn = fn,
        hooks = {
            before : [],
            after : []
        };

    function hookableFunction() {
        var args = [].slice.call(arguments, 0),
            i = 0,
            fn;
        for (i = 0; !!hooks.before[i]; i += 1) {
            fn = hooks.before[i];
            fn.apply(this, args);
        }
        ifn.apply(this, arguments);
        for (i = 0; !!hooks.after[i]; i++) {
            fn = hooks.after[i];
            fn.apply(this, args);
        }
    }

    hookableFunction.addHook = function (type, fn) {
        if (hooks[type] instanceof Array) {
            hooks[type].push(fn);
        } else {
            throw (function () {
                var e = new Error("Invalid hook type");
                e.expected = Object.keys(hooks);
                e.got = type;
                return e;
            }());
        }
    };

    return hookableFunction;
}
0 голосов
/ 01 января 2015

Я не знаю, будет ли это полезно. Вам нужно изменить исходную функцию, но только один раз, и вам не нужно продолжать редактировать ее для ловушек

https://github.com/rcorp/hooker

0 голосов
/ 24 апреля 2013

Вот что я сделал, может быть полезно в других приложениях, таких как:

//Setup a hooking object
a={
    hook:function(name,f){
        aion.hooks[name]=f;
    }
}a.hooks={
    //default hooks (also sets the object)
}; 

//Add a hook
a.hook('test',function(){
    alert('test');
});

//Apply each Hook (can be done with for)
$.each(a.hooks,function(index,f){
    f();
});
...