I'm looking for an elegant way to build an event-driven architecture where modules (scripts) are completely independent and decoupled from each other and rely only on Mediator for communication. Let's consider typical example:
var SomeModule = function () {...};
SomeModule.init = function ()
{
Mediator.register ('SomeEvent', this.onSomeEvent, this);
Mediator.register ('OtherEvent', this.onOtherEvent, this);
};
SomeModule.onSomeEvent = function (data)
{
var x = data.x;
var y = data.y;
// ........
Mediator.dispatch ('ThirdEvent', {name: 'Liza', gender: 1});
// now all modules registered for 'ThirdEvent' will get their callbacks executed
};
Typical Mediator operates as a dictionary routing calls from event name to according callback array to execute callbacks from. Now the problem lies in optimization: Mediator.dispatch (...) introduces dynamic objects and therefore polymorphic code which will remain slow and unoptimized when executed in V8. Some events will be fired 60 times per second and will have multiple callbacks, so it will surely benefit from optimizations.
What is the most elegant way to make this code monomorphic without introducing large boilerplate code for every new event?
EDIT: replaced .bind(this) with supplying this as context param as suggested in the comments.
60 times per second for this sort of operation is nothing.
That said if you want to optimize it, the most obvious bottleneck here will be definitely calling a function created using the native .bind function. You should always (I am not joking about always)use a homemade bind as the first thing when optimizing:
function bind(fn, ctx) {
return function() {
return fn.apply(ctx, arguments);
};
}
This doesn't do the same thing as Function.prototype.bind at all, which is the point: it does exactly what you would want bind to do and nothing more.
Related
In one place we use eventEmitter to generate events. Actually it's the very common way.
eventEmitter.emit('started', data, date);
In the other place we try to catch it. Everything is pretty clear when using arrow functions. 'data' and 'date' are passed to the function as arguments
someInstanse.event.on('started', (data, date) => {
//crazy stuff here
})
But how this notaion in fact works? We determine 3 args with emitter and now we really have only event string and a function instead
someInstance.event.on('started', function(data, date) {
});
I suppose that before adding arrow functions it was the only way to call anonymous functions
This is the typical publish/subscribe design pattern. And it is really determined by how the emit and how subscribers respond to the event are implemented under the hood.
Basically, in the publish function, you want to call every subscriber(on) functions, provided with the information with publish(emit). Below is just some pseudo-code.
function publish(type, ...args) {
// for each of the subscribers of that type
for (let i = 0; i < pubsub[type].length; i++) {
// you could do (this provides the listener with type)
subscribers[i](type, ...args)
// or you could do (the subscriber doesn't know the type)
subscriber[i](...args)
}
}
I wrote a minified pub/sub pattern in github, if you want to take a look. I think it's extremely helpful to help you understand this issue.
https://github.com/thomasyimgit/pubsub/blob/master/index.js
What I want looks like this:
function bindFunctions(bindFunction, callbackFunction) {
// Add binding so that I can call the callbackFunction if the bindFunction is called
}
function log(message) {
console.log(message);
}
function notifyUser() {
alert('Something');
}
bindFunctions(log, notifyUser);
log('Error'); // Now the notifyUser-functions should be called and "Something" printed to the alert-box
bindFunctions($('.element').click, function() {/* CODE */}); // Or this: but I don't know if this is even possible because this is not the event-function but the binding-function of the click-event
Important: I have no influence on the bindFunction so it's not possible to implement a trigger there.
It's an attachment of a callback on any kind of existing function. Do you know how or if this is possible?
I believe you're looking at it the wrong way. What you need is some good old dependency inversion. Whatever code needs log has to receive it from a higher-level component (e.g. the composition root of your application). You're then free to implement a straightforward wrapper that calls notifyUser and inject it instead of the actual log.
I've linked some articles taking an OO perspective, but feel free to translate to a more functional model (the approaches are equivalent). In your case, you're using closures (which are, under a certain light, "equivalent" to objects with a single anonymous method).
The way you have to do to add a callback to a function is this:
var foo = function(number, callback){
number += 2;
callback(number);
}
foo(2, function(result){
window.alert(result)
});
https://jsfiddle.net/6dpz88md/
Good luck
similar question here, but not fully answered
I am trying to put my web application inside a class for better encapsulation, so it may be better used as a plugin on other sites. But for events that need callbacks (mouseover, etc), they lose the context of the calling class object. The only solution I've found so far is to used JQuery's $.proxy method. I have created a simple example:
https://jsfiddle.net/swb909/dcad5m3b/
I would expect there to be a slightly more elegant native solution / strategy for this, but I cannot find one. What is the best practice for this?
Edit: Perhaps a better example would be a JQuery callback for an AJAX request, a Node.JS emit callback, or a setTimeout call - both all be out of the context / scope of the calling class object.
The callbacks will not inherit the context of the calling function.
Example: http://jsfiddle.net/swb909/h29aqmyg But I am more concerned
with the overall practice of maintaining a context, as this problem
applies to all callbacks from classes and things like setTimeout or
Node.JS emit callbacks, etc
This depends on expected context with handler . Both overBox , outBox methods appear to use selector $('#box') to call .css() instead of $(this) ; this could be set to hoverbox to have access to hoverbox properties within event handler without affecting expected results.
Try utilizing Function.prototype.bind()
var hoverbox = function(id, overColor, outColor) {
this.changeColors(overColor, outColor);
$(id).mouseover(this.overBox.bind(this))
$(id).mouseout(this.outBox.bind(this))
}
hoverbox.prototype.overBox = function (e) {
console.log(e, this)
$('#box').css('background', this.overBoxColor)
}
hoverbox.prototype.outBox = function () {
$('#box').css('background', this.outBoxColor)
}
hoverbox.prototype.changeColors = function (overColor, outColor) {
this.overBoxColor = overColor;
this.outBoxColor = outColor;
$('#box').css('background', outColor)
}
var hb = new hoverbox('#box', 'blue', 'red');
$('#changeColors1').click(function(){hb.changeColors('yellow', 'green')})
$('#changeColors2').click(function(){hb.changeColors('purple', 'orange')})
$('#changeColors3').click(function(){hb.changeColors('blue', 'red')})
jsfiddle https://jsfiddle.net/h29aqmyg/3/
I have tried searching through a lot of S.O. pages but nothing has touched EXACTLY on this top while also NOT USING JQUERY.... I am trying to stick to pure JavaScript as I want to learn it 115% before advancing my current knowledge of JQuery.
I have an object called ScreenResizeTool like this...
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg);
}, true);
}
and a method like this...
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
My trouble is probably obvious to an experienced JavaScript user but I am having trouble not making this into a messy dirty awful OOP set. I have done various tests to show and prove to myself that the this inside the addEventHandler changes when it becomes bound to the window. This much I assumed before testing but I was able to see that once window.resize event happens the listen method is gone and not a part of the global window variable....
I have also tried adding a this capture such as this.me = this inside the object constructor however it also couldn't see the me variable once it ran. Once the window took the function over it no longer knew anything about the me variable or any reference to my class methods....
I am aware that I could separate this differently but my goal here is to learn how to fully encapsulate and use as many clean OOP structures as possible as I just came from the .NET world and I need it in my life.
I am also aware that I could make messy calls and or store this object or access to the methods inside the window variable but that seems outright wrong to me. I should be able to fully encapsulate this object and have its events and methods all implemented in this class structure.
I also know that the currImg variable is not going to be seen either but lets start small here. I assume once I figure out my incorrect train of thought on scope for JavaScript I should be fine to figure out the currImg problem.
I know there's 1000 JavaScript programmers out there waiting to rip me a new one over asking this simple question but I gotta know...
Thoughts anyone?
this inside a function bound to a DOM Object (like window) will always refer to that object.
this inside a constructor function will always refer to the prototype.
A common practice to circumvent the this issue, as you mentioned, is to cache it in a variable, often called self. Now you want the variables and properties of your object available after instantiation, so what you need is the return keyword, more specifically to return the parent object itself. Let's put that together:
function ScreenResizeTool() {
var self = this;
// method to instantiate the code is often stored in init property
this.init = function() {
window.addEventListener('resize', function() {
self.listen(); // self will refer to the prototype, not the window!
}, true);
};
return this;
}
ScreenResizeTool.prototype.listen = function() { // Dummy function
var h = window.innerHeight, w = window.innerWidth;
console.log('Resized to ' + w + ' x ' + h + '!');
};
Pretty easy huh? So we have our prototype now, but prototypes can't do anything if there's not an instance. So we create an instance of ScreenResizeTool and instantiate it with its init method:
var tool = new ScreenResizeTool();
tool.init();
// every time you resize the window now, a result will be logged!
You could also simply store the listen & init methods as private functions inside your constructor, and return them in an anonymous object:
function ScreenResizeTool() {
var listen = function() { ... };
var init = function() { ... };
// in this.init you can now simply call listen() instead of this.listen()
return {
listen: listen,
init: init
}
}
Check out the fiddle and make sure to open your console. Note that in this case I'd rather use the first function than the second (it does exactly the same) because prototypes are only useful if you have multiple instances or subclasses
The whole concept of this in JavaScript is a nightmare for beginners and in my code I usually try to avoid it as it gets confusing fast and makes code unreadable (IMHO). Also, many people new to JavaScript but experienced in object-oriented programming languages try to get into the whole this and prototype stuff directly though the don't actually need to (google JS patterns like IIFE for example as alternatives).
So looking at your original code:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg); // global function listen?
}, true);
}
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
First off, you probably mean addEventListener instead. In its callback you refer to listen but as a global variable which would look for it as window.listen - which doesn't exit. So you could think to do this:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
this.listen(currImg); // what's this?
}, true);
}
As you want to use the prototype.listen function of ScreenResizeTool. But this won't work either as the event listener's callback function is called with a different this and not the this that is your function scope.
This is where something comes in which makes most programmers cringe, you have to cache this, examples from code I've seen:
var _this = this;
var that = this;
var _self = this;
Let's just use the latter to be able to refer to the function within the event callback:
function ScreenResizeTool(currImg) {
var _self = this;
window.addEventListener('resize', function() {
_self.listen();
}, true);
}
Now this will actually work and do what you want to achieve: invoke the prototype.listen function of ScreenResizeTool.
See this JSFiddle for a working example: http://jsfiddle.net/KNw6R/ (check the console for output)
As a last word, this problem did not have anything to do with using jQuery or not. It's a general problem of JS. And especially when having to deal with different browser implementations you should be using jQuery (or another such library) to make your own code clean and neat and not fiddle around with multiple if statements to find out what feature is supported in what way.
Just wanted to know if it was a good JavaScript practice.
Let's say I have many Web pages that all call an initialization function "init()", would it be the right thing to use an IIFE inside my pattern to run the function everytime the script is loaded?
var foo = (function() {
var bar = "something";
(function init() {
// Do something crazy that's gonna be the same across all my web pages
// like adding an event listener or something
// ...
document.write('page init...');
}());
function privatePage1() {
// This stuff is gonna be used only in page1.html via foo.privatePage1
document.write('page 1' + bar);
}
function privatePage2() {
// This stuff is gonna be used only in page2.html via foo.privatePage2
document.write('page 2' + bar);
}
return {
privatePage1: privatePage1,
privatePage2: privatePage2
}
}());
This is a pretty subjective area, but here's my take:
When you use the module pattern, you're providing a contained set of functionality to the rest of your code. It's essentially a mini-library.
In general, I wouldn't expect a library to do anything when I load it, other than initialization steps that are entirely internal to the library (e.g. setting up the configuration, instantiating a few necessary objects, etc) - nothing that actually affects the DOM or otherwise significantly alters the environment (which is why I've never been entirely comfortable with libraries like Date.js or Prototype that change the prototypes of basic objects).
There are a couple of reasons for this, but the main one is that I don't want to have to worry about the load order of my libraries/modules, other than simply managing dependencies. Independent modules shouldn't affect each other at all. When you manipulate the DOM in your module at load time, sooner or later you'll realize that another piece of your code is expecting the DOM to be in a certain state at a certain time, and that you now have to care about whether you load your module before or after that time. This is an extra bit of complexity that's essentially hidden in the script tag that loads your module.
The other issue here is portability and adaptability. Maybe you'll want to use your module in another project with another DOM setup. Maybe you'll want to pass a different DOM element or config variable to the init() function on a specific page. If you execute init() automagically, you lose the opportunity for configuration.
So what I generally do is to set the init() method as an attribute of the returned module object:
var foo = (function() {
function init() {
// Do something crazy that's gonna be the same across all my web pages
}
//...
return {
init: init,
// etc
}
}());
and then call it as needed elsewhere in my code:
foo.init();
Yes, this adds an extra line of redundant code to the initialization for all my pages (though this is probably just one other script anyway, so the added weight is all of 11 characters). But it allows me a more fine-grained control over when the module is initialized, and offers a hook for configuration arguments when I (inevitably) determine I need them later.
Is the init() function the same across web pages? If so, this is what I'd do:
var foo = (function()
{
init();
return {};
}());
If not, I don't see a reason to use an IIFE, and would simplify your original code like so:
var foo = (function()
{
/* body of the original IIFE here */
return {};
}());