Let object "invoke" function in JavaScript - javascript

I wasn't sure of the title, but I hope you will be able to help me.
I want to use kinda the same pattern that jQuery uses for their event handlers, like click/hover/blur etc, where you use this inside the event handler to get the object.
How do I accomplish this?
// The event handler
var handler = function() {
alert(this); // how do I set "this" in the trigger?
}
// The function that triggers the event handler
var trigger = function(handler) {
var o = someObject;
if($.isFunction(handler)) handler(); // how do I set "o" as the this-reference in handler?
}

You use the "call" or "apply" functions on the Function prototype:
handler.call(thingThatShouldBeThis, arg, arg, arg);
or
handler.apply(thingThatShouldBeThis, [arg, arg, arg]);
Call takes a list of arguments like a regular function, and apply wants the arguments in a single array.
Old versions of some browsers didn't have "apply", but I don't know whether that's worth worrying about now.

Related

How to pass arguments in a function reference

I'm looking for a way to save reference to two this objects in one function called after an event triggers in jQuery - this reference to the object the method is defined in (so I can use this.anotherObjectFunction()) and this reference to the object that triggered the event - so that I can use $(this).someJQueryFunction later on. The way I'd like to do it is by passing a this (function object) reference as an argument to the function. Unfortunately, the function is to be called by jQuery, not me, so it's passed as a reference, i.e.
someFunction: function()
{
...
cell.$el.on('click', 'li.multiselect-option', this.myClickFunction);
...
},
myClickFunction: function(objectReference)
{
//It should be able to call methods of that object.
objectReference.anotherFunction();
//And reference to the clicked item.
$(this).html("Don't click me anymore!!!");
}
I'm aware of the fact that I can do something like
cell.$el.on('click', 'li.multiselect-option', myFunction.bind(this));
...
myClickFunction: function(event)
{
this.anotherFunction();
$(event.currentTarget).html("Don't click me anymore!!!");
}
But this workaround doesn't really answer the question as it doesn't show how to pass additional arguments and in the future there may be a necessity to pass another (no, I don't want to register them as fields in the object).
No anonymous functions are allowed unless they can be easily removed with cell.$el.off() function that will remove them and only them (there are some other function associated with the same objects and events at the same time and they should remain intact).
UPDATE:
By no anonymous functions I mean solutions like:
var self = this;
cell.$el.on('click', 'li.multiselect-option', function() {
self.MyClickFunction(self, this);
});
They will not work because I'll have to use cell.$el.off() with the function reference (3-argument prototype) to remove this single function and only it, leaving other functions bound to both the same element and event.
Jquery .on event has option to pass the argument as parameter in event handler like this
cell.$el.on('click', 'li.multiselect-option', {arg1:'arg1' , arg2:'arg2'} , myFunction);
...
myClickFunction: function(event)
{
alert(event.data.arg1);
alert(event.data.arg2);
this.anotherFunction();
$(event.currentTarget).html("Don't click me anymore!!!");
}
Passing data to the handler
If a data argument is provided to .on() and is not null or undefined,
it is passed to the handler in the event.data property each time an
event is triggered. The data argument can be any type, but if a string
is used the selector must either be provided or explicitly passed as
null so that the data is not mistaken for a selector. Best practice is
to use a plain object so that multiple values can be passed as
properties.
or another way
cell.$el.on('click', 'li.multiselect-option', function() {
myClickFunction("hai" , "bye");
});
myClickFunction: function(arg1, arg2)
{
alert(arg1);
alert(arg2);
}
And I would also suggest a plugin-free solution to the question "how to supply a parameter to function reference"
Example
function x(a){
console.log(a);
}
setInterval('x(1)',1000);

Where does an anonymous function gets its arguments

I'm learning to develop Windows 8 style applications with the help of a book. The chapter I'm reading focuses on HTML, CSS and JavaScript languages for developing. The application displays in a ListView the images you have in the My Pictures Folder and deletes them when the user clicks or taps an image. Here is the code that implements the deletion of an image in the ListView:
var lv = document.getElementById('lv');
lv.addEventListener('iteminvoked', function (eventObj) {
eventObj.detail.itemPromise.then(function (listViewItem) {
var binding = files.dataSource.createListBinding();
binding.fromIndex(listViewItem.index).then(function (dataItem) {
var key = dataItem.key;
files.dataSource.remove(key);
binding.release();
});
});
});
My question is, where does the eventObj parameter of the anonymous function in the addEventListener method gets its value? I have found a similar question asked here: Passing arguments in anonymous functions in JavaScript, but i cannot fully understand it. I searched the documentation for addEventListener on MSDN but it just says it takes an event handler function, but it doesn't say anything about the parameters. Thanks in advance.
It's rather simple: whatever function internally calls that callback passes the arguments. See, addEventListener tells the executing Javascript engine to call the callback function you specify whenever an event occurs. The javascript engine saves your anonymous function in some variable - and cann call it later on using that exact variable, passing any number of arguments.
To illustrate it, consider something like this the internal function that handels events (purlely fictional, just to illustrate how it could be done):
var callbacks = [];
function addEventListener(newEvent, newCallback) {
callbacks.push({event : newEvent, callback : newCallback});
}
function handleEvent (someEvent) {
for (var i = 0 ; i < callbacks.length ; i++ ) {
if (callbacks[i].event == someEvent.name) {
callbacks[i].callback(someEvent);
}
}
}
Some more explanation:
As javascript is a so-called "functional language", functions are just values of variables.
function someFunc () {}
is actually just some kind of shortcut (technically it's not, but it does the same thing) for
var someFunc = function () {}
This having said, it's of cours possible to associate multiple names with one function:
var someFunc = function () {}
var sameFunc = someFunc;
var stillSame = somefunc;
var alsoSame = stillSame;
and you can call that function using any of those names, including passing arguments of course:
var someFunc = function (arg) { alert(arg); }
var sameFunc = someFunc;
sameFunc("It worx");
You can even call a function without ever naming it:
(function () {alert("test")})();<
or
(function (arg) { alert(arg); })("test")
Using this concept to perversion finally leads (long way to go however) to things like the y-combinator.
Event handlers may be attached to various objects including DOM
elements, document, the window object, etc. When an event occurs, an
event object is created and passed sequentially to the event
listeners.
Source: https://developer.mozilla.org/en-US/docs/Web/API/Event
An event listener or event handler can be an anonymous function or named function, it really doesn’t matter. The point is that it’s the event interface that defines the event object that is passed to the handler.
To find out exactly the event property from the event you are using, please refer to the windows docs: http://msdn.microsoft.com/en-us/library/windows/apps/br211827.aspx
The arguments recieved by the event listener are sent from the dispatchEvent, i.e. when the event dispatched it passes an event object to your handler.
Refer to this documentation on how to create and dispatch the event. The event object can vary in structure to convey information to the eventhandler to execute necessary steps. So in your case when you do lv.dispatchEvent(newevent) this sends an newevent as eventObj to your event handler.
Keep in mind there can be multiple eventhandlers listening to an event so the browser maintains a stack for the eventlisteners running them sequentially with each of them passed eventObj.
Anonymous function is no different from a named function. In JavaScript functions are first-class objects meaning regular objects. So you can pass them like regular objects(numbers,strings) without having to name them. Only thing is reuse becomes an issue.
What you need to understand this code is to rewrite it a bit:
var lv = document.getElementById('lv'),
invokeHandler = function (eventObj) {
var promiseFullfilled = function (listViewItem) {
var binding = files.dataSource.createListBinding(),
anotherPromiseFullfilled = function (dataItem) {
var key = dataItem.key;
files.dataSource.remove(key);
binding.release();
};
binding.fromIndex(listViewItem.index).then(anotherPromiseFullfilled);
};
eventObj.detail.itemPromise.then(promiseFullfilled);
};
lv.addEventListener('iteminvoked', invokeHandler);
This code works just the same, however it is now obvious that addEventListener or then actually do not know anything about the callback functions they are passed with. They can, however, use Function.prototype.call or Function.prototype.apply to apply arguments:
// This is PSEUDOCODE, event model actually works in a totally different way
HTMLElement.prototype.addEventListener = function(eventType, callback, bubbles) {
// callbacks is some internal collection for this specific element, probably available via a closure, looks something like:
// {
// 'someEventType': [callback1, callback2],
// 'someOtherEvent': [callback1, callback3, callback4]
// }
callbacks[eventType].push(callback);
}
// This is called whenever an event is triggered on an element
HTMLElement.prototype.dispatchEvent = function(event) {
callbacks[event.type].forEach( function(callback) {
return callback.call(this, event); // the callback is called with 'this' set to the element DOM object, and 'event' is the first argument
});
// then it can bubble or cancel depending on the event type and callback results
}
it is a CustomEvent, and all the process is like that:
//you add a anonymous function to a specific listener
lv.addEventListener('iteminvoked', function (eventObj) {
console.log(eventObj===myEvent);
});
//somewhere in your code a CustomEvent gets created based on "iteminvoked" key
var myEvent = new CustomEvent("iteminvoked", {
itemInfo: {
name: "yourItem"
},
bubbles: true,
cancelable: false
});
//somewhere when an item gets invoked this code raise the `iteminvoked` trigger
lv.dispatchEvent(myEvent);
all the functions that are passed as a listener are stored based on the key, something like:
var observers = {
"iteminvoked" : [f1, f2],
//other keys
}
it doesn't have anything to do with not having name, the function object is stored in the some kind of array. and dispatchEvent goes thru the array and invokes all the functions, and pass the myEvent as their parameter. It is a Observer pattern, implemented in javascript, I have implemented it once in my own javascript library like:
var lv = /*your element*/;
if(observers["iteminvoked"]){
for(var i=0;i<observables["iteminvoked"].length;i++){
var func = observables["iteminvoked"][i];
var o = func.call(lv, myEvent);
//this line is to support return false
if(o!==undefined && o===false) break;
}
}
as you can see it is dispatchEvent resplonsiblity to invoke all the observers, and your function no matter it has name or not gets invoked with the lv as the this context and myEvent as the parameter.

javascript passing event in closure

I was trying the following:
f.addEventListener('submit',(function(frm){
var func = (function(e){somefunction(e,frm);})(e);
})(f),false);
But this is failing. I want to pass the form (f) as a static reference and the dynamic event object to the named function 'somefunction'.
What I have above isnt working, what is the right syntax for passing both?
The issue is that each of the functions is being called right away, with undefined actually being passed to addEventListener().
You'll want to instead return one of the functions without its calling parenthesis so the event can call it later:
f.addEventListener('submit', (function (frm) {
return function (e) {
someFunction(e, frm);
};
})(f), false);
Though, with event bindings, you may not necessarily need the closure, as the <form> will be the context (this) of the function passed:
f.addEventListener('submit', someFunction, false);
function someFunction(e) {
var frm = this;
// ...
}
not saure exactly what you are trying to do but, to looks like you are trying to manually pass in the form via the event handler. Instead save a reference and just refer to it in the handler such as
f.addEventListener('submit',function(){
var func = function(e){
somefunction(e,f);
};
},false);
you shouldn't need the self executing functions unless I am missing your intent here

Bind and unbind a bound function to an event

I am using EventEmitter2 as message bus internal within my application. Now I need to bind and unbind some event handlers. As I want them also to bind them to a given context, I end up with the following syntax:
messageBus.on('foo::bar', _.bind(eventHandler, this));
The problem is that I need to unbind them at a later point in time, so I wrote:
messageBus.off('foo::bar', _.bind(eventHandler, this));
Unfortunately this does not work, as _.bind each time returns a new instance of the wrapper function. Now of course I could run _.bind once and bind the wrapped function, such as this:
var fn = _.bind(eventHandler, this);
messageBus.on('foo::bar', fn);
messageBus.off('foo::bar', fn);
This works perfectly well, but if you have a few event handlers, the code quickly starts to get less readable than necessary.
How might you solve this without the need to externalize the call to the bind function? Is there an alternative function that always returns the same wrapper in case you call it multiple times and the function as well as the context are the same?
Underscore.js provides a bindAll method just for this use-case, from the documentation:
Binds a number of methods on the object, specified by methodNames, to be run in the context of that object whenever they are invoked
Failing that, you could make use of a closure which always returns the same bound function when invoked, ie:
function getOrCreateBoundEventHandlerFor(eventType, callback) {
// Initialise the handler map if it's not already been created.
this._boundEventHandlerMap = this._boundEventHandlerMap || {};
// If no handler was mapped, create a new one.
if (this._boundEventHandlerMap[eventType] === void 0) {
this._boundEventHandlerMap[eventType] = _.bind(callback, this);
}
return this._boundEventHandlerMap[eventType];
}

Should I avoid creating this JavaScript closure?

This may be a bit abstract but I'm trying to get my head round JavaScript closures etc. Take the following code:
function MyObj() {
var me = this;
this.foo = function(bar) {
// Do something with 'bar'
}
// Set up lots of local variables etc.
// ....
$(window).load(function() {
// Add a delegated click handler to specific <input> elements
$(document).on('click.myobj', 'input.special', function() {
// Do something with the <input> that triggered the click event
me.foo(this);
});
});
}
var myObj = new MyObj();
The anonymous function passed to that is bound to the click event creates a closure that references me. What I want to know is whether it's better to do something like this instead (to avoid the closure):
$(window).load(function() {
// Add a delegated click handler to specific <input> elements
(function(localMe) {
$(document).on('click.myobj', 'input.special', function() {
// Do something with the <input> that triggered the click event
localMe.foo(this);
});
})(me);
});
Is this a better approach, or am I being overly paranoid about creating a closure? Alternatively, is there a "third way"?
EDIT
Additionally, would it be better to do something like this:
$(window).load(function() {
// Add a delegated click handler to specific <input> elements
$(document).on('click.myobj', 'input.special', {localMe : me}, function(event) {
// Do something with the <input> that triggered the click event
event.data.localMe.foo(this);
});
});
The latter is (AFAIK) more efficient, but probably not measurably so unless used in a tight loop.
The reason is that all variable dereferencing must follow the scope chain. In the latter case, the variable localMe can be found in the anonymous function's parameter list.
In the former case, the variable isn't found there, but in the outer scope. This traversal up the scope chain takes extra time.
Anonymous functions are massively used in javascript now (as arguments and as an immediate function for scopes/closures). There's no performance problem with that.
But you can have a problem of code reading maybe. Because when you see a variable, you must check where the variable is from. But no big deal here.
And in your second example, you still have a closure "break". Because in your anonymous function in the click, you use the localMe variable. And the localMe is an argument of a fonction outside of your fonction.
// Here, 'me' is a direct local variable.
$(window).load(function() {
// Here, we are in an anonymous fonction, so 'me' is not a direct variable anymore. But you still can access it.
// Add a delegated click handler to specific <input> elements
(function(localMe) {
// Here, 'localMe' is a direct local variable.
$(document).on('click.myobj', 'input.special', function() {
// We are in an anonymous function, so 'localMe' is not a direct variable anymore.
// Do something with the <input> that triggered the click event
localMe.foo(this);
});
})(me);
});
If you really want to avoid a closure "break", you should bind your function to your object. But note that not every browser support the bind method on functions.
You will always create a closure if you bind the event from the constructor. In fact, you even need the closure to preserve the reference to your instance. However, you might do something like this:
function MyObj() {
this.foo = function(bar) {
// Do something with 'bar'
}
// Set up lots of local variables etc.
// ....
}
var myObj = new MyObj();
$(function() {
$(document).on('click.myobj', 'input.special', function() {
myObj.foo(this);
});
});
If you do only create a singleton instance of your constructor, it won't matter anyway.
I would probably do it this way:
var bind = function( fn, me ) { return function() { return fn.apply(me, arguments); }; },
Object = (function() {
function Object() {
this.handler = bind(this.handler, this);
// Add a delegated click handler to specific <input> elements.
$(document).on("click.myobj", "input.special", this.handler);
}
Object.prototype.foo = function( bar ) {
// Do something with "bar".
};
Object.prototype.handler = function( event ) {
// Do something with the <input> that triggered the click even.
return this.foo(event.currentTarget);
};
return Object;
})();
var obj = new Object();
This skips the uses of closures and iifes, using .apply instead. Not sure if it is more efficient or not, but it is another option.

Categories

Resources