Reputation: 1009
I want to invoke function X everytime any other function is invoked. I want to keep this as generic as possible.
having these two functions
function x(){ console.log("invoke BEFORE"); }
function someFunction(something){ console.log(something); }
When someFunction
is invoked
someFunction("testoutput");
I want the console to output this:
>> invoke BEFORE
>> testoutput
I also want this behaviour to apply to any function of a certain object.
For example:
var myFunctions = {
first:function(){/* do something */},
second:function(){/* do something else*/}
}
myFunctions.before(function(){/do something before/});
Anyone know a solution?
EDIT:
I have come up with a solution like this:
Object.prototype.before = function(x){
for(var key in this){
if(typeof this[key] === "function")
this[key] = (function(x, f) {
var g = f;
return (function() {
x();
return g.apply(this, arguments);
});
}(x, this[key]));
}
}
var test = { func: function(){console.log("test")}};
test.before(function(){console.log("before")});
test();
results in:
>> before
>> test
YAAAYYY
how do you like this?
Upvotes: 0
Views: 88
Reputation:
In my opinion, event binding is more flexible than function wrapping since you can remove x
whenever you want. Here is a possible implementation:
// Observable
var Observable = {};
Observable.create = function (options) {
var observable = { events: {} };
var events = options.events || [];
var i, l = events.length;
for (i = 0; i < l; i++) {
observable.events[events[i]] = [];
}
return observable;
};
Observable.one = function (observable, event, handler) {
Observable.on(observable, event, function f () {
Observable.un(observable, event, f);
handler.apply(this, arguments);
});
};
Observable.on = function (observable, event, handler) {
observable.events[event].push(handler);
};
Observable.un = function (observable, event, handler) {
observable.events[event].splice(
observable.events[event].indexOf(handler), 1
);
};
Observable.emit = function (observable, event, params) {
var handlers = observable.events[event];
var i, l = handlers.length;
if (!params) params = {};
params.source = observable;
for (i = 0; i < l; i++) {
handlers[i].call(observable, params);
}
};
Observable.observeMethod = function (observable, name) {
var meth = observable[name];
var before = 'before' + name.toLowerCase();
var after = 'after' + name.toLowerCase();
observable.events[before] = [];
observable.events[after] = [];
observable[name] = function () {
var ret;
Observable.emit(observable, before);
ret = meth.apply(observable, arguments);
Observable.emit(observable, after, { value: ret });
return ret;
};
};
// init
var printer = init({
sayHello: function () {
this.print('Hello World.');
},
sayHi: function (e) {
this.print('Hi ' + e.pseudo + '.');
},
print: function (msg) {
print(msg);
}
});
var clock = init({
tid: null,
events: ['tick'],
stop: function () {
clearTimeout(this.tid);
},
start: function () {
var me = this;
var time = 0;
clearTimeout(this.tid);
(function tick () {
me.tid = setTimeout(tick, 1000);
me.emit('tick', { time: time++ });
})();
}
});
// demo: printer
printer.on('afterprint', printNewline);
printer.on('beforesayhello', printBullet);
printer.sayHello();
printer.sayHello();
printer.un('beforesayhello', printBullet);
printer.sayHello();
// demo: clock
clock.on('tick', function (e) {
if (e.time) printer.print('tick ' + e.time);
if (e.time === 3) this.stop();
});
clock.one('afterstop', clock.start);
clock.start();
// helpers
function init (obj) {
obj = initObservable(obj);
obj.one = function (event, handler) {
Observable.one(this, event, handler);
};
obj.on = function (event, handler) {
Observable.on(this, event, handler);
};
obj.un = function (event, handler) {
Observable.un(this, event, handler);
};
obj.emit = function (event, params) {
Observable.emit(this, event, params);
};
return obj;
}
function initObservable (obj) {
var k, observable;
observable = Observable.create({
events: obj.events
});
for (k in observable) {
obj[k] = observable[k];
}
for (k in obj) {
if (typeof obj[k] === 'function') {
Observable.observeMethod(obj, k);
}
}
return obj;
}
function printBullet () {
print('• ');
}
function printNewline () {
print('<br />');
}
function print (html) {
document.body.innerHTML += html;
}
Upvotes: 0
Reputation: 12214
This is a bad idea that will make understanding and debugging your program much harder.
You can use what in Python is called "monkey-patching" to achieve this:
(function() {
{
var origSomeFunction = someFunction;
someFunction = (function() {
x();
return origSomeFunction.apply(this, arguments);
});
}();
This works because I changed the (global) name someFunction
to refer to a new function that I defined. Within the closure of that function I keep a reference to the original function that you want to pass the call on to.
Upvotes: 1