If I call the function once like this
var button = document.querySelector('button');
button.addEventListener('click', once);
function once() {
console.log('one');
button.removeEventListener('click', once);
}
It's calling only once.
But if I called like this once()
var button = document.querySelector('button');
button.addEventListener('click', once());
function once() {
console.log('one');
button.removeEventListener('click', once());
}
Exception throws
Exception: InternalError: too much recursion
Could you please explain why this is happening.
() after function name invoke's the function. So as button.addEventListener('click', once()); you are bind the return value of once() method which is undefined.
And since once() is called recursively without any break statement, you are getting the InternalError: too much recursion.
You should pass the function reference.
button.addEventListener('click', once);
For Additional Info:
Pointer Vs Delegates
If you put () after the name of a variable holding a function, then you call the function.
If a function calls itself, then it will get called, call itself, call itself again and so on unto infinity. This is recursion. Doing something unto infinity will cause a computer to run out of memory, so it is undesirable.
JavaScript engines prevent this by throwing an exception when you try it.
(There are exceptions, of course, a function that calls itself conditionally can be very useful).
The first code is correct, because you register the function to be called.
The second code tries to register the result of the function call once(). This means you actually execute the function when you only want to register it. Now, in your function body, you do the same to deregister the callback. Here again, you call the function you are already executing and hence, you recurse infinitely.
Related
I am trying to call a nested function which is stubbed.
I am using sinon for stubbing.
function logger(){
function log(msg){
sendMsg(msg)
}
var sendMsg = function (msg){
console.log("original func")
}
return {
log:log,
sendMsg:sendMsg
}
}
I am stubbing the 'sendMsg' func and trying to call it from log function, but instead of the stubed function, the original one is called.
The weirdest thing is that when I call sendMsg with this.sendMsg it calls the stub.
Im sure there is a JS principle im not aware of, Ill be glad you can point me to one..
Thanks
The log() function refers directly to the symbol sendMsg to call the function. The only sendMsg visible to log() is that local variable to which the the "real" function is assigned. Changing the value of the "sendMsg" property on that returned object cannot affect that; log() will always call the "real" sendMsg() function.
If log() were written to call this.sendMsg instead, then your stub function would be called.
I have the following jQuery code:
function next() {
//some code here
}
function previous() {
//some code here
}
$("#next").click(function(){
next();
});
$("#previous").click(function(){
previous();
});
This works, but this doesn't:
$("#next").click(next());
$("#previous").click(previous());
Why is this happening? Is there a problem in my code, or is this just a thing with jQuery? Note: #next and #previous refer to two buttons in my html file.
The callback should be a reference to the function.
Why $("#next").click(next()); doesn't work?
func() is a function call and not a reference, which is why it is called immediately.
This,
$("#next").click(function(){
next();
});
is a preferable way in case you need to pass arguments.
Else,
$("#next").click(next) //notice just the signature without ()
This works (if the functions next and previous are defined):
$("#next").click(next);
$("#previous").click(previous);
In this case the next and previous are also callback functions, the difference between the two is,
when you call this line
$("#next").click(next()); the function is executed immediately, and you are passing the result of the next function to the eventHandler of jQuery.
and in this case
$("#next").click(next); you are passing the function next to the EventHandler of jQuery.
Btw.: in the jQuery API Documentation (https://api.jquery.com/click/) it shows all parameters for the click function and the required types it states: "...handler Type: Function( Event eventObject ) A function to execute each time the event is triggered. ..."
try like this you will get your answer,
function next() {
//some code here
}
function previous() {
//some code here
}
$("#next").click(next);
$("#previous").click(previous);
working demo jsfiddle Example
What is going on there is a little bit obscured by the syntax of anonymous functions function() { ... }. What you are doing by that is passing a function, without calling it. And I want to explain how this works:
If you have a simple function
function next() { return 5 };
It will simply return the value 5, if you call it from somewhere:
a = next(); // value of a will be 5
But what you can do too, is to pass the whole function to a. This is possible, because functions in JavaScript are actually objects:
a = next;
b = a(); // value of b will be 5
If you look at the syntax, it shows you, that putting parentheses () at the end of a function invokes it, and returns the return value. While the naked string, without parentheses hands you the function itself.
So what is a callback now, and what does click() like to get as a parameter? A callback function is a function, that gets called later; we actually hand it over, to get called later. click() would like to get such a function as parameter, and it should be clear now, that we have to pass the function without parentheses, to enable click() to call it later, instead of just passing a 5 to it.
$("#next").click(next);
So how does then the initial syntax with the anonymous function work?
function() { next(); }
actually wraps your next() into another function, which is anonymous – because it does not have a name – but is working in the same way as a named function. You can even set a variable by it:
a = function() { next(); } // a will be the anonymous function that calls next()
But calling that function a() will return nothing, because the anonymous function does not return a value (To be exactly: every function call in JavaScript is returning at least undefined, but that's a technical detail).
It can even be called immediately by putting parenthesis at the end of it:
a = function() { return next(); }() // value of a will be 5
Adding the return there will make sure, the return value of next() will be passed through the anonymous function.
This should make clear why
$("#next").click(function(){ next(); });
is working, and why
$("#next").click(next());
is not, but
$("#next").click(next);
will be a good solution.
$("#next").click(next); would work. Notice parenthesis are not required as the function/callback handler should be passed as a parameter.
Why does this work:
$("#oneButton").click(function () {
alert("hello");
});
But this doesn't work?
$("#oneButton").click(
alert("hello");
);
The anonymous function route gives a function to be executed later. The second example merely executes code immediately, never waiting for a click event to take place.
The $.fn.click method expects to call a function on your behalf, which will handle the future click event(s). If you give it a number, or a string, or undefined, it will do nothing.
The alert function returns undefined, so your second example is nothing more than:
$("#oneButton").click(undefined);
Note that you don't need to use an anymous function, and in most cases you probably shouldn't. You can use a named function, which helps with debugging down the road:
function handle ( event ) {
alert( "Clicked" );
}
$("#oneButton").click(handle); // or .click(function handle ( event ) { ... });
In this example, we're passing the function reference to our click event to serve as the handler. Now that jQuery knows of our function, it will execute it when the click event is raised on #oneButton.
Can anyone explain why this is causing my app to crash? It always crashes on the second iteration of the loop.
function FetchMetaData () {
alert("Am I being fired");
}
var timer= setInterval(FetchMetaData(),10000);
It's not "crashing"; you're just calling the function once. You should pass the function itself to setInterval(), not the result of calling the function:
var timer = setInterval(FetchMetaData, 10000);
When you write it as FetchMeData(), that means that the function should be called right then and there, and that whatever value it returns should be what's passed to setInterval(). Sometimes that makes sense, but in this case you need to pass a reference to your function. You do that in JavaScript by simply using the name of the function without calling it.
I have a function that I want to subscribe it to a event queue. When it executes this function I need it to unsubscribe from the queue.
functionToExecute is the function I want to execute.
onDOMReady subscribes the attachDOMHandler function which in turn executes functionToExecute and unsubscribes itself. I have:
onDOMReady : function(functionToExecute){
subscribe(this.attachDOMHandler(functionToExecute));
},
attachDOMHandler : function(functionToExecute) {
unsubscribe(this.attachDOMHandler(functionToExecute));
functionToExecute();
}
Everything works fine when I have the following:
onDOMReady : function(functionToExecute){
subscribe(functionToExecute, this, true);
},
However I need the function to unsubscribe after it is executed. My plan was to use the attachDOMHandler function which executes the function and has the unsubscribe behaviour.
When I run the former I get a "Uncaught RangeError: Maximum call stack size exceeded" in the Chrome console. It looks like it gets stuck in an infinite loop which makes sense since I keep running attachDOMHandler in the unsubscribe which gets caught in a loop.
I basically need to subscribe a function that I specify by parameter passing. When the function is executed it unsubscribes.
Hope I have explained this ok.
You don't want to call the function directly:
function f() { // the function to subscribe unsubscribes itself
// and calls the actual function
unsubscribe(f);
functionToExecute();
}
subscribe(f);