What's a standard way of associating unique properties to anonymous JavaScript functions while creating them so that these properties can be accessed within the functions themselves when they are executed (i.e. run time)?
A scenario is this:
Say I have events which I want to bind to dynamically generated anonymous functions.
var events = ['connect','disconnect','error','connect_failed'];
for(a in events){
var handler = function(){
// console.log(arguments.callee.custom); // Not reliable 'cos callee is supposedly deprecated
console.log('<event-name>');
};
handler.custom = events[a];
$(window).on(events[a],handler);
}
Since using arguments.callee is deprecated and not guaranteed to be optimal on all platforms. What's the recommended way of achieving something similar to this?
You can use handler.custom in your function, too:
var handler = function() {
console.log(handler.custom);
console.log('<event-name>');
};
To prevent scoping issues in case of asynchronous callbacks, you can create a newly scoped handler function by wrapping your code in a closure:
for(a in events){
(function() {
var handler = function(){
console.log(handler.custom);
console.log('<event-name>');
};
handler.custom = events[a];
$(window).on(events[a],handler);
})();
}
EDIT: just realized you can use forEach as well (although that also suffers from browser compatibility issues):
events.forEach(function(event) {
var handler = function() {
...
};
handler.custom = event;
$(window).on(event,handler);
});
Named function expressions give a reference to the function itself only within the function scope:
var events = ['connect', 'disconnect', 'error', 'connect_failed'];
for (var a in events) {
var handler = function inside() {
console.log(inside.custom); // not deprecated
console.log('<event-name>');
};
handler.custom = events[a]; // now will stay with function even if renamed or moved
$(window).on(events[a], handler);
}
However, Internet Explorer 8 and below will not parse named functions correctly - Juriy Zaytsev explains the exact issue behind them in more detail: http://kangax.github.io/nfe/#named-expr
If you really have to target IE8 and below, either stick with arguments.callee or use conditional compilation for IE, which is basically IE conditional comments for JavaScript.
You could do something like this:
var events = ['connect','disconnect','error','connect_failed'];
function assignHandler(eventType, custom) {
$(window).on(events[a], function() {
console.log(custom);
});
}
for(a in events){
assignHandler(events[a], events[a]);
}
The magic of JS closures means that the anonymous function assigned as an event handler inside assignHandler() will be able to access the custom argument of assignHandler() even after assignHandler() has finished.
However, since you seem to be using jQuery's .on() method you don't need to implement anything yourself because jQuery already has functionality for this:
.on( events [, selector ] [, data ], handler(eventObject) )
Note the optional third argument is data. Within the event handler this can be accessed as event.data.
for(a in events){
var handler = function(e){
console.log(e.data);
console.log('<event-name>');
};
var custom = events[a];
$(window).on(events[a], null, custom, handler);
}
I know what you mean...
you want event name when the event is triggered and want to know which event has triggered at run time in function... Am I Right? then you can use Event Object received in handler...
DEMO
var events = ['connect','disconnect','error','connect_failed'];
for(a in events){
var handler = function(e){
console.log(e.type);
console.log('<event-name>');
};
$(window).on(events[a],handler);
}
for(a in events){
$(window).trigger(events[a]);
}
Related
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.
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.
What value do we get by using closures in JavaScript for implementing callbacks and and event handler attachment . I know jQuery uses this pattern extensively but want to understand why ?
Assigning a closure as a callback gives you access to the scope which it is defined within. It also helps keep your namespace clean. Other libraries allow you to bind object methods as event handlers, giving the event access to the object, which is a pretty elegant alternative solution. For instance, maybe something like this:
// an object definition
var Accordion = function () {
this.clickCounter = 0;
};
Accordion.prototype.click = function () {
this.clickCounter += 1;
alert( this.clickCounter );
};
// create object
var myAccordion = new Accordion();
// function to bind DOM element events to javascript object methods
function methodBind ( element, eventType, myObject, myMethod ) {
$( element ).bind( eventType, function(e) {
e.preventDefault();
myObject[myMethod ].call(myObject);
} );
}
// bind buttons on an element to activate a correlating object method
$( '.Accordion' ).find( '.button' ).each( function() {
methodBind( this, 'click', myAccordion, 'click' );
} );
Although my implementation also uses closures haha.
Yes, you might endup in what Esailija describes, but you might also save some kittens. Event handlers in closures give you access to local scope. So instead of
var uselessMap = {};
function myListener(e) {
alert(uselessMap[e.target.id].foo);
}
items.forEach(function(item) {
var el = document.createElement('div');
el.innerText = item.name;
el.id = item.id;
el.addEventListener('click', myListener, false);
})
You only need this:
items.forEach(function(item) {
var el = document.createElement('div');
el.innerText = item.name;
el.addEventListener('click', function(e) {
alert(item.foo);
}, false);
})
I'm not sure to really understand what you need.
Closures is not jQuery system, it's the main core or Javascript.
The thing is Javascript is a language event based. While you make an ajax call, your javascript will continue running and not stopping expecting the result. So you need to have a callback method to be able to do something once it's done.
I know it may be frustrating when coming from a blocking language like PHP where you call a function and the line below is executed when the action is done. But it's the Javascript philosophy. Do not wait while you can do other thing, just be able to pop when this or this event is called.
Typically, for events like click/change on input/submit a form etc, you cannot wait and block all your code until some action popped out. So attach a listener to each event you want to ear about and continue your way.
First of all, I'm attempting to use faux namespaces in my JavaScript program like so:
// Ish.Com namespace declaration
var Ish = Ish || {};
Ish.Com = Ish.Com || {};
// begin Ish.Com.View namespace
Ish.Com.View = new function() {
var privateVariable;
this.publicFunction = function() {
this.publicFunction2()
};
this.publicFunction2 = function() { ... };
};
I'm not crazy about using this to call other functions, but up to recently, it has worked. However, I've added event listeners to some elements, and they interpret this to be the target object.
I know I can use the full namespace instead of this to call functions inside of my listeners (Ish.Com.View.publicFunction2()), but the listeners often call one function, which calls another, and another. I'd need to use the entire namespace in nearly every function call.
How can I get namespaces to work nicely with Event Listeners? I'd also be interested in a better way of implementing namespaces, since using this.publicFunction2() is clunky.
I'm very interested in best-practices, and learning how to write a well architected application in JavaScript. However, frameworks are out of the question until I gain a more thorough understanding of JavaScript.
Seems like I've been answering every question this morning the same way :-)
You can use ".bind()":
var eventHandler = yourObject.someFunction.bind(yourObject);
That'll guarantee that this will refer to "yourObject" whenever the "eventHandler" is called by anything.
The "bind()" function is there on the Function.prototype object in newer browsers. The Mozilla docs include a solid implementation of "bind()" you can use to patch older browsers.
What "bind()" does is return you a new function that explicitly arranges for this to be bound as you stipulate. You can also pass arguments to be passed in, if you like. An alternative to using "bind()" is to wrap the function call in your own anonymous function:
var eventHandler = function() { yourObject.someFunction(); };
I don't know if i have understood completely your question.
Would this suit your needs ?
var obj = new function() {
var scope = this;
this.fun1 = function() {
return scope.fun2();
}
this.fun2 = function() {
//do sth
}
};
This is caused by variable context and closure. "this" always refers to the current object. The event function is an object itself and "this" refers to that object. If you need to refer to the parent object you can use bind as described previously or you set the parent "this" to a variable and use that in your event function.
// Ish.Com namespace declaration
var Ish = Ish || {};
Ish.Com = Ish.Com || {};
// begin Ish.Com.View namespace
Ish.Com.View = new function() {
var privateVariable, thisObj=this;
this.publicFunction = function() {
thisObj.publicFunction2()
};
this.publicFunction2 = function() { ... };
};
Where SomeMethod could have:
function SomeMethod(item)
{
item.setAttribute('name', item.id);
}
Instead of:
function SomeMethod(itemId)
{
var someItem;
someItem = document.getElementById(itemId);
someItem .setAttribute('name', someItem .id);
}
Silly example, but the idea is not to send in the id itself, but the actual control calling the method. I swear this can be done but have had no luck searching... partially because I'm not even sure what to search on.
I thought it was self, but self doesn't seem to be what I want when the script I have runs.
Use the this Keyword.
You actually don't need to pass this as an argument to your function, because you've got a click event object that you can access. So:
<script>
function clickEventHandler(event) {
if (!event) {
event = window.event; // Older versions of IE use
// a global reference
// and not an argument.
};
var el = (event.target || event.srcElement); // DOM uses 'target';
// older versions of
// IE use 'srcElement'
el.setAttribute('name', el.id);
}
</script>
I tend to use this approach in all function calls from HTML attributes:-
onclick="SomeMethod.call(this)"
Then in the javascript do:-
function SomeMethod()
{
this.setAttribute('name', this.id);
}
This has a distinct advantage when you may also assign directly to event handler properties in Javascript code:-
document.getElementById("someID").onclick = SomeMethod
If SomeMethod took the context element as a parameter it would very awkward to set up:-
function(id) {
var elem = document.getElementById(id)
elem.onclick = function() { SomeMethod(elem); }
}("someID");
Worse yet this would be memory leaking closure.
At this point: SomeMethod(this) - this returns window object so do not use it. The right way to use this keyword is making it context relevant, so use SomeMethod.call(this).