I want to create my own signal emitter or callback storage, call it whatever you want.
Here is what I come up so far:
var DetonationCallback = function detonationCallback() {
detonationCallback.callbacks = [];
detonationCallback.add = function(callback) {
detonationCallback.callbacks.push(callback);
};
for(var i = 0; i < detonationCallback.callbacks.length; ++i) {
callback[i](arguments);
}
};
Basically I have two problems now which I can not tackle. The first one is how can I move
detonationCallback.callbacks = [];
detonationCallback.add = function(callback) {
detonationCallback.callbacks.push(callback);
};
outside of the function? And another question is how can I pass all the arguments which were passed into the detonationCallback just in the same order into callback[i]?
Will be grateful for any answer.
Additional info: when done with the implementation of the type I would like to use it as follows:
var callback = new DetonationCallback();
function f1() {
}
function f2(firstArg, secondArg) {
}
callback.add(f1);
callback.add(f2);
callback();
First, make detonationCallback an Object
var detonationCallbacks = {};
detonationCallbacks.callbacks = [];
detonationCallback.clear = function() {
detonationCallback.callbacks = [];
}
detonationCallback.add = function(callback) {
detonationCallback.callbacks.push(callback);
};
Second, make your detonate a function in detonationCallback
detonationCallback.detonate = function() {
for(var i = 0; i < detonationCallback.callbacks.length; ++i) {
this.callback[i](arguments);
}
};
If you want/need to use new, just create an object prototype for this
I'm not sure why you wan't to move the add function outside but how about using a class instead?
Also note the use of .apply() to send several arguments with an array.
class DetonationCallback {
constructor() {
this.callbacks = [];
}
add(callback) {
this.callbacks.push(callback);
}
call(args) {
this.callbacks.forEach(function(callback) {
callback.apply(this, args);
});
}
}
var callback = function(arg1, arg2) {
console.log(arg1, arg2);
}
var callback2 = function(arg1) {
console.log(arg1);
}
var handler = new DetonationCallback();
handler.add(callback);
handler.add(callback2);
handler.call(['arg1', 'arg2']);
No need to write this yourself:
When using node you can use the built in EventEmitter lib.
https://nodejs.org/api/events.html
There is also a port for the Browser
https://github.com/Olical/EventEmitter
Related
I am iterating through an array of functions and am suppose to call each function USING THE OBJECT AS THE CONTEXT and I don't know how to do it - tried apply but it doesn't work-I guess because I don't fully understand this, apply etc.
Happy about some help!
function calling(obj,arr){
for (var i=0; i<arr.length;i++){
arr[i].apply(null,obj);
}
return arr;
}
var fnA = [
function () {
this.yes = true;
},
function () {
this.no = false;
}
];
var obj = {};
calling(obj, fnA);
obj.yes; //should return true
Are you sure you read the documentation of Function.prototype.apply() correctly?
thisArg, which sets the execution context, is the first parameter of apply, not the second:
arr[i].apply(obj);
As second parameter, you can optionally pass an array of arguments that the function should be called with.
This is example using recursion approach.
If your array of functions should be immutable you need a For loop instead of recursion (or make a copy of initial array).
var functions = [
function() { this.yes = true; },
function() { this.no = false; }
];
var emptyObject = {};
function delegate(object, methods) {
if (methods.length) {
methods.splice(0, 1)[0].apply(object);
delegate(object, methods);
}
}
delegate(emptyObject, functions);
console.log(emptyObject.yes);
console.log(emptyObject.no);
Suppose I have a function proxyThrough like this:
function proxyThrough(parentClass, childObjPropertyName, methodName) {
parentClass.prototype[methodName] = function() {
this[childObjPropertyName][methodName].apply(this[childObjPropertyName], arguments);
};
}
childPropertyName and methodName are both strings, and it looks up the functions by name.
I know that this will not survive minification as a result.
How can I get functions like this to survive minification?
Example
This is what I am doing currently:
var BaseView = require('./BaseView');
var FooView = require('./FooView');
function BarView() {
this._fooView = new FooView();
}
BarView.prototype = Object.create(BaseView.prototype);
BarView.prototype.constructor = BarView;
BarView.prototype.anAction = function() {
this._barView.anAction.apply(this._barView, arguments);
};
BarView.prototype.anotherAction = function() {
this._barView.anotherAction.apply(this._barView, arguments);
};
This is what I would like to do instead:
var BaseView = require('./BaseView');
var FooView = require('./FooView');
function BarView() {
this._fooView = new FooView();
}
BarView.prototype = Object.create(BaseView.prototype);
BarView.prototype.constructor = BarView;
function proxyThrough(parentClass, childObjPropertyName, methodName) {
parentClass.prototype[methodName] = function() {
this[childObjPropertyName][methodName].apply(this[childObjPropertyName], arguments);
};
}
['anAction', 'anotherAction'].forEach(proxyThrough.bind(null, BarView, '_fooView'));
I guess it depends on how the minifier works, but if it renames the same property name consistently, you could use a helper function to get the minified property name:
function minifiedName(obj) {
for (var prop in obj) {
return prop;
}
}
[
minifiedName({anAction: null}),
minifiedName({anotherAction: null})
].forEach(proxyThrough.bind(null, BarView, '_fooView'));
I have a Object based on some closure, and want to implement event scheme here:
var class1 = function(val1)
{
var val = val1;
//------ want to call a method of Object of class1--------
var self = this;
setTimeout(function()
{
self.onEvent();
}, 1000);
//----------------
return {
f1: function()
{
return val;
},
onEvent: function()
{
console.log('not implemented yet. Override');
}
};
};
var obj1 = class1(5);
console.log(obj1.f1()); //5
obj1.onEvent(); //not implemented yet. Override
obj1.onEvent = function()
{
console.log('event fired');
}
got error, and I know the reason, and I need a solution:
5
not implemented yet. Override
/....../app.js:9
self.onEvent();
^
TypeError: Object #<Object> has no method 'onEvent'
It is possible if this bind with addEventListener scheme like this:
(The idea based on
Implementing events in my own object
)
var class2 = function()
{
var _this = this;
_this.events = {};
var fireEvent = function(name, args)
{
if (!_this.events.hasOwnProperty(name)) return;
if (!args || !args.length) args = [];
var evs = _this.events[name];
var l = evs.length;
for (var i = 0; i < l; i++)
{
evs[i].apply(null, args);
}
};
setTimeout(function()
{
fireEvent('testEvent', ['hello'])
}, 1000);
return {
addEventListener: function(name, handler)
{
if (_this.events.hasOwnProperty(name))
_this.events[name].push(handler);
else
_this.events[name] = [handler];
}
};
};
var obj2 = class2();
obj2.addEventListener('testEvent',
function(data)
{
console.log('event fired: ' + data);
});
event fired: hello
However, I prefer not to use addEventListener but .onEvent() scheme.
Is it possible? Perhaps it is possible using call/apply.
Thanks for your advice.
In your first block of code, you are returning an object, which is different from this or self.
You don't necessarily have to return this in your constructors but you should assign your functions on the returned object. If you create a variable for the object you want to return, you can use it in your setTimeout callback like so:
var class1 = function(val1)
{
var val = val1;
var obj = {
f1: function()
{
return val;
},
onEvent: function()
{
console.log('not implemented yet. Override');
}
};
setTimeout(function()
{
obj.onEvent();
}, 1000);
return obj;
};
For extra style points, you might want to capitalize the name of your constructors (and perhaps use new to instantiate them to make things clearer to your readers).
Let's say I have a function named fna() that does a simple thing such as:
var fna = function(ar) {
console.log("argument: ", ar);
return "return value is argument too: " + ar;
}
fna() is coded by some other developer and I can't access to it. He didn't bother casting any events and when it is called, I have to be aware of it. Hopefully, his method is accessible by window.fna().
I want some additional code to be executed. Let's say, add this console.log
var fna = function(ar) {
console.log("Hola, I am some additional stuff being rewired");
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
And I want this to be executed even when called from fnb() by some other part of the code.
var fnb = function() {
return fna("Bonjour, I am fnb and I call fna");
}
Here is a way I found, using the utils.rewire() method. utils is just some utility belt, and it could be added to your favorite framework as a plugin. Unfortunately, it only works on Firefox.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(functionFullName, callback) {
var rewired = window[functionFullName];
console.log("%s() is being rewired", functionFullName)
window[functionFullName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
Use it like this.
utils.rewire("fna",function(){
console.log("Hola, I am some additional stuffs being rewired");
});
This seems to work such as shown in this jsbin, but (and here is my question:) How do I rewire obja.fna()?
var obja = {
fna = function(ar) {
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
};
I cannot make it work to rewire the some.object.method() method.
Extra bonus question: Is there a more cleaner way to do this? Out-of-the-box clean concise and magic library?
Refactor rewire into a rewireMethod function which acts on any given object:
var utils = utils || {};
utils.rewireMethod = function (obj, functionName, prefunc) {
var original = obj[functionName];
obj[functionName] = function () {
prefunc();
return original.apply(this, arguments);
};
};
Note that rewire can now be written as:
utils.rewire = function (functionName, prefunc) {
utils.rewireMethod(window, functionName, prefunc);
};
Then you just call it as:
utils.rewireMethod(obja, "fna", function () {
console.log("Hola, I am some additional stuff being rewired");
});
Note that nothing special is required if you have a method like window.ideeli.Search.init(). In that case, the object is window.ideeli.Search, and the method name is init:
utils.rewireMethod(window.ideeli.Search, "init", function () {
console.log("Oh yeah, nested objects.");
});
Add a parameter to rewire that is the object containing the function. If it's a global function, pass in window.
var utils = utils || {};
// let's rewire a function. i.e. My.super.method()
utils.rewire = function(object, functionName, callback) {
var rewired = object[functionName];
console.log("%s() is being rewired", functionName)
object[functionName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
utils.rewire(some.object, "method", function(){} );
You can simply use a closure to create a generic hook function that allows you to specify another function to be called immediately before or after the original function:
function hookFunction(fn, preFn, postFn) {
function hook() {
var retVal;
if (preFn) {
preFn.apply(this, arguments);
}
retVal = fn.apply(this, arguments);
if (postFn) {
postFn.apply(this, arguments);
}
return retVal;
}
return hook;
}
So, for any function that you want to hook, you just call hookFunction and pass it the function you want to hook and then an optional pre and post function or yours. The pre and post function are passed the same arguments that the original function was.
So, if your original function was this:
var fna = function(ar) {
console.log("argument:",ar);
return "return value is argument too:"+ar;
}
And, you want something to happen every time that function is called right before it's called, you would do this:
fna = hookFunction(fna, function() {
console.log("Hola, I am some additional stuff being rewired right before");
});
or if you wanted it to happen right after the original was called, you could do it like this:
fna = hookFunction(fna, null, function() {
console.log("Hola, I am some additional stuff being rewired right after");
});
Working demo: http://jsfiddle.net/jfriend00/DMgn6/
This can be used with methods on objects and arbitrary nesting levels of objects and methods.
var myObj = function(msg) {
this.greeting = msg;
};
myObj.prototype = {
test: function(a) {
log("myObj.test: " + this.greeting);
}
}
var x = new myObj("hello");
x.test = hookFunction(x.test, mypreFunc2, myPostFunc2);
x.test("hello");
Based on Claudiu's answer, which seems to be the most appreciated way, here is a solution using a for loop and proxying the context... But still, I find this ugly.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(method, callback) {
var obj = window;
var original = function() {};
var tree = method.split(".");
var fun = tree.pop();
console.log(tree);
// Parse through the hierarchy
for (var i = 0; i < tree.length; i++) {
obj = obj[tree[i]];
}
if(typeof(obj[fun]) === "function") {
original = obj[fun];
}
var cb = callback.bind(obj);
obj[fun] = function(ar) {
cb();
return original.apply(this, arguments);
}
}
Well, this looks strange. Consider this
function wrap(fn, wrapper) {
return function() {
var a = arguments;
return wrapper(function() { return fn.apply(this, a) })
}
}
Example:
function foo(a, b) {
console.log([a, b])
return a + b
}
bar = wrap(foo, function(original) {
console.log("hi")
var ret = original()
console.log("there")
return ret
})
console.log(bar(11,22))
Result:
hi
[11, 22]
there
33
To wrap object methods, just bind them:
obj = {
x: 111,
foo: function(a, b) {
console.log([a, b, this.x])
}
}
bar = wrap(obj.foo.bind(obj), function(fn) {
console.log("hi")
return fn()
})
The entire code snipped is:
var observer = {
addSubscriber: function(callback) {
this.subscribers[this.subscribers.length] = callback;
},
removeSubscriber: function(callback) {
for (var i = 0; i < this.subscribers.length; i++) {
if (this.subscribers[i] === callback) {
delete(this.subscribers[i]);
}
}
},
publish: function(what) {
for (var i = 0; i < this.subscribers.length; i++) {
if (typeof this.subscribers[i] === 'function') {
this.subscribers[i](what);
}
}
},
make: function(o) { // turns an object into a publisher
for(var i in this) {
o[i] = this[i];
o.subscribers = [];
}
}
};
It depends on how it is called. I see it is part of an object literal called observer.
observer.make(o) would mean this == observer.
observer.make.call(otherObj, o) would mean this == otherObj.
new observer.make(o) would make a new object to be this
So it would do something like this.
var model = {
name: 'bike',
id: 4,
gears: 7
};
observer.make(model);
//now model has methods from observer
model.addSubscriber(someListener);
model.publish('gearsChanged');
"this" refers to "observer" assuming that is the object in which it was invoked (and in 99% of cases it is);
so: observer.addSubscriber
in the method addSubscriber, "this" will refer to "observer".
When you have objects within objects (or nodes) it can be confusing to resolve "this":
observer = {
node: $("myDiv"),
callIt: function(){
// note "this.node" - node belongs to observer
this.node.onclick = function(){
// "this" now refers to the "node" object
// onclick was invoked by node
}
}
}
this, is how you refere at the scope of a function. it's the function itsel.!!! this example in prototypejs framework is quite handy.
http://api.prototypejs.org/language/function/prototype/bind/
for example if you the following code.
function foo(){
//here this is foo
var x = {}; //object
var me = this;
var img = new Image();
img.load = function(){
//but here this is img.load.. is the scope of the function =)
// if you want to use the x object you have to assing this FOO a global variable is why you use me = this;
me //is foo :P
}
}