Consider the following jQuery implementation defined using an object literal...
$(function() {
var myObject = {
methodOne: function()
{
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
myObject.methodTwo();
}
);
},
methodTwo: function()
{
$('#element').animate(
{'marginLeft': '-50px'},
'slow',
function() {
myObject.methodOne();
}
);
}
} // End myObject
myObject.methodOne(); // Execute
});
For the record, the above code works just as expected. What I don't understand is why a subtle and seemingly harmless change like the following...
methodOne: function()
{
$('#element').animate(
{'marginLeft': '50px'},
'slow',
myObject.methodTwo() // No more anonymous function
);
},
... to both methodOne and methodTwo causes a browser error stating too much recursion. What's the difference between how I've declared my callback? Also, if I bring back the anonymous function declaration, but modify the object reference to look like this...
methodOne: function()
{
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
this.methodTwo(); // assuming 'this' refers to 'myObject'
}
);
},
... I get one good pass through methodOne and upon callback my browser freaks out because it cannot find methodTwo. My guess is that I fell out of scope somewhere, but I can't rightly decide where. Your insight is much appreciated!
Lets break this down
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
myObject.methodTwo();
}
);
In this example you pass a function object as a callback. That function object is called when animation is done. It has the myObject object shared via closure, so it can easily find it and call a method on it. Awesome!
$('#element').animate(
{'marginLeft': '50px'},
'slow',
myObject.methodTwo() // No more anonymous function
);
Here something different is going on. As the callback here you are actually passing the return value of myObject.methodTwo(), and not the actual function object. So since methodTwo() doesn't return anything, then undefined is actually passed as your callback. Meaning that the animate() function thinks there is no callback.
So maybe you meant to try this!
$('#element').animate(
{'marginLeft': '50px'},
'slow',
myObject.methodTwo // No more anonymous function or invocation
);
Well this still wouldn't work. Now you are passing a function object for the callback, yes, but it will lose context (this). It turns out that when you invoke a function object on it's own, then this is the global object. Check this out:
var obj = {
foo: function() { console.log(this) }
};
obj.foo() // logs obj
var func = obj.foo;
func() // logs window (the global object in a browser)
So you cant pass a function object directly in for a callback that is supposed to be invoked like a method with an object as the receiver. Internally to the animate() method there, it executes callback() for you, which is a call that does not preserve the value of this for you at all.
So why didnt this one work?
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
this.methodTwo(); // assuming 'this' refers to 'myObject'
}
);
When an anonymous function is invoke as the callback, just like breaking off a method, this defaults to the window object. So this code actually calls window.methodTwo(), which doesn't exist, and it explodes.
So the accepted standard JS way to do this your first way.
someFunc(arg1, arg2, function() {
someObj.someMethod()
});
That should always work, even though it seems wasted because you invoking 2 function to do one thing. But as you are discovering it's the least error prone.
Learning how this works in JS is a painful experience, but when you get it, you find that the rules are pretty straight forward and easy to manipulate.
If you still dont like that, you can do some tricky js magic. Like underscore.js bind() method.
someFunc(arg1, arg2, _.bind(someObj.someMethod, someObj));
This returns a function that will always run with someObj as this which can safely be used as a callback.
Or if you want to try out CoffeeScript it has a fat arrow to preserve context, wich compiles to JS similar to what the underscore.js bind() method does.
someObj =
foo: ->
someFunc arg1, arg2, =>
this.bar()
bar: ->
alert 'got the callback!'
someObj.foo()
In this case, the => style of function declaration preserves the context of the scope where it appears, so this can be safely used.
when you give jquery the function to execute you are actually passing in a function pointer and not what you want to execute.. it basically does exec on the function reference that you passed and thus when you changed it to obj.method() it longer understood it..
The problem with the myObject.methodTwo() is that myObject.methodTwo() is evaluated before being passed as a function. Javascript and by extension jQuery expects a function pointer in cases like these. I.E. setTimeout function.
In the below usage, an anonymous function is created (just a block of statements) and is registered as the callback function. Note the use of 'function(){'. The anonymous function does exactly one thing: calls myObject.methodOne()
function() {
myObject.methodOne();
}
regarding you second question in the callback function the this refers to a different object and not your original object anymore and that is why that failed. if you want to pass your this reference to the callback function read this: http://thomasdavis.github.com/tutorial/anonymous-functions.html
When you put the callback as myObject.methodTwo(), you are actually invoking methodTwo then and there. Then inside methodTwo it does the same thing by calling methodOne...which calls methodTwo, which calls methodOne, and so on. So you have an infinite recursion.
When you wrap that same line of code inside an anonymous function, it does not invoke the function immediately. You are now passing the function itself, not the returned value. The function using the callback then determines when (if ever) to actually invoke the function.
As zzzz and Paul stated, you're calling the function and passing its return value, rather than passing a callback. But you don't need to wrap that in an anonymous function to make it work - simply remove the () after the method and it'll work fine.
This.methodTwo here is looking for local reference, This belong to current scope, so method two is define out side the current scope you can use . I just quick view the code and find the following suggestion.
methodOne: function()
{
var that = this;
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
that.methodTwo(); // assuming 'this' refers to 'myObject'
}
);
}
Related
So basically why do I have to use this kind of method in these kind of situations in particular?
function Example(callback) {
// What's the purpose of both 'call()' and 'null'?
callback.call(null, "Hello")
}
Exemple(function(callback) {
alert();
})
I've figured it out this syntax in a open project code but I haven't found out why it works yet.
You don't need to use call() in this situation. call() is used when you need to set the value of this in a function. If you aren't doing that, just call the function like you would any other function. For example:
function Example(callback) {
// call the function passed in
callback("Hello")
// this also works, but there's no benefit here
// because the callback doesn't care about `this`
// callback.call(null, "Hello")
}
// pass a callback function to Example
Example(function(text) {
console.log(text);
})
You would use call in situations where the callback function needs a value for this. For example:
function Example(callback) {
let person = {name: "Mark"}
// set this in the callback to the person object
// and the execute it with the argument "Howdy"
callback.call(person, "Howdy")
}
Example(function(greeting) {
// the callback function depends on having this.name defined
console.log(greeting, this.name);
})
This looks pointless at the first glance, however, there might be valid reasons for this syntax, for example:
the callback relies explicitly on this being null and not some global default
the callback is not necessarily a function, it can be a "function-alike" object that provides a .call method
callback.call(... is used consistently through the application, no matter if this is needed or not
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.
This is a simple question. Here is my code:
$(document).ready( function () {
func1( "foo", callback);
function callback(param){
alert(param+" is my name");
}
function func1(name, cb) {
cb(name); // alerts "foo is my name"
callback("bar"); // alerts "bar is my name"
}
});
I want to know:
Which one of the function calls inside func1 is the correct callback and why?
Or are they both correct?
Isn't callback("bar"); a normal function call?
Callbacks are meant to let a caller specify what a function should do at some defined point in that function's execution. The function being called shouldn't know the name of that callback function ahead of time. So they'll often be passed to a function as an argument and the function that's supposed to call the callback should just invoke that argument.
When you call callback("bar") in func1, you're totally missing the point of callbacks. You may be invoking the function that you happen to use as a callback, but the point of callbacks is that func1 isn't supposed to know about that. It's just supposed to call the function that's been passed in as an argument (cb). When I'm calling func1 I should be able to pass a completely different callback function and func1 should just call that function without knowing what its name is (it may not even have one!).
The "correct" way is cb(name).
callback("bar"); is directly invoking the callback function where as cb(name); calls the reference passed to the func1,
cb(name); seems to be the correct way here.
First one. Function calls another one which has been pased as a parameter.
It seems like most jquery methods follow this this form for callbacks:
$(SUBJECT).method(function() {
//do stuff
}, /*callback here*/ function(){
//do stuff
});
like for instance
$(foo).click(function() {
$(bar).fadeIn(300, function(){
//call back here
});
});
fiddle
How can I get this function to pass by reference with this code?
var Class = function() {
var callback1;
var callback2;
function buildStuff(data, callback) {
element.onclick = function() {
doStuff(callback);
};
}
function doStuff(callback) {
callback();
}
return {
"setCallback1":function(fn) {
callback1 = fn;
},
"setCallback2":function(fn) {
callback2 = fn;
},
//may rebuild with different data, but same callback
"buildFoo":function(data) {
buildStuff(data, callback1);
},
//may rebuild with different data, but same callback
"buildBar":function(data) {
buildStuff(data, callback2);
}
};
}
function main() {
var object = Class();
object.setCallback1(function() {
//do stuff
});
object.setCallback2(function() {
//do something else
});
}
When you actually click on the element, callback is undefined. I would expect it to be the anonymous function I set it to with the setCallback function because the user click occurs after I call the setCallback function.
Thanks!
EDIT: Thanks for the input. I should have mentioned I need to be able to dynamically set what callback equals. So, I can't just eliminate the callback parameter from buildStuff.
EDIT2: Very sorry for the confusion; I realize my example was a bit too out of context to show what I am doing. buildStuff is actually a private member function (using the module pattern) that is called repeatedly. Depending on what is being built, it needs a different callback. The callback is actually set outside of the class (well, module pattern class), so it has to be dynamic. I've updated my code, and again, sorry for the bad example.
The click handler you create in buildStuff creates a closure over the local variables. You pass callback to your buildStuff function, but at the time you pass it, it's undefined. As this shadows the other callback variable, you always see this value of undefined, rather than the state of the other callback variable.
Instead, don't pass a parameter to buildStuff, and the closure will be created, and will capture the callback variable you want.
function buildStuff() {
element.onclick = function() {
doStuff(callback);
};
}
Imagine this;
Your global variable callback points to a value (in this case undefined).
When you buildStuff in main(), you pass the value pointed to by callback (undefined) as a parameter to buildStuff
Your click handler creates a closure over local variables + other variables in scope (note that the local callback shadows the global callback). callback in your event handler is now undefined.
You then setCallback. setCallback changes the value the global callback variable points to using the = operator. The global callback and local callback now point to different values, which is why you don't see the callback in the event handler update.
What you want to do in this situation is to change the value pointed to by callback, so other variables pointing there also update, but JavaScript doesn't let you do this.
Yes, but you've already called buildStuff before setCallback.
The contents of callback at the time (undefined) will be used.
If you want to call buildStuff with different callbacks, just do that, and eliminate the redundant setCallback:
function buildStuff(callback) {
element.onclick = function() {
doStuff(callback);
};
}
function doStuff(callback) {
callback();
}
function main() {
buildStuff(
function() {
//do something
}
);
}
This is best explained by a code example, so can anyone explain why (in technical terms) the anonymous function passed to test doesn't get called after the jQuery hide event?
UPDATE: Not that is really matters for this example what this is referring to, but for clarity lets say test function is in the global scope and this is an anchor element.
test(this, function() {
alert('Called by anonymous function!');
});
function test(object, callback) {
$(object).hide('slow', callback);
}
Changing:
$(object).hide('slow', callback);
To:
$(object).hide('slow', callback());
works. Is this because callback isn't a named function in the current context or global window object?
Your code should look more like this
test(this, function() {...});
You're not calling an anonymous function in your code, but I'm not sure what that syntax actually does
Check out this fiddle of a working example http://jsfiddle.net/L4NxD/2
Edit, made more edits to the fiddle to better duplicate original code. Use http://jsfiddle.net/L4NxD/1 and just http://jsfiddle.net/L4NxD/ to get earlier versions.
This is the correct way to do it.
http://jsfiddle.net/6dFm6/6/
Calling remove after the hide animation will remove the object and result in the animation callback not being called.
The reason it works in jsfiddle.net/6dFm6/1 is because the callback is executed at runtime and the value is passed and called during the callback event.
See these articles for varying level of clarity on what is returned by 'functionName()' as the callback. It appears 'undefined' is returned by the call, however the execution still happens at the correct time.
When to use () after a callback function name?
In JavaScript, does it make a difference if I call a function with parentheses?
Callback function - use of parentheses
do I need parenthesis when using a named function as an jquery ajax success callback
javascript syntax: function calls and using parenthesis