Timeout with bind, call & apply methods - javascript

Up until now, I've always used var self = this before creating a function that would need access to its parent. However the bind() method seems like a more appropriate way to do so and I'm exploring that option, along with the apply() and call() methods.
This is what I came up with to compare all three:
jsFiddle
(function(){
this.say = function(text){
console.log(text);
}
this.run = function(){
console.clear();
setTimeout(function(){
this.say('bind');
}.bind(this), 1000);
setTimeout(function(){
this.say('call');
}.call(this), 1000);
setTimeout(function(){
this.say('apply');
}.apply(this), 1000);
}
this.run();
})();
But the script leaves me with some questions:
Why don't the call() and apply() methods respect the timeout like the bind() method does and which one should I use?
Is there any difference between the following syntaxes which behave similarly:
setTimeout( function(){ this.say('bind'); }.bind(this) , 1000);
setTimeout( (function(){ this.say('bind'); }).bind(this) , 1000);
setTimeout( (function(){ this.say('bind'); }.bind(this)) , 1000);

You're missing a key point here (but the key point is very obfuscated in documentation).
You know whenever you write a function like this:
function something() {
// ... whatever
}
It's just a function reference sitting there by itself. It hasn't been called yet.
When you write something like this:
(function something() {
})()
Or this:
function something() { ... }
something();
You've now called the function. Those are two very completely different concepts.
In the first one where the function is just sitting there without having been called, it's referred to as a function reference, and that's exactly what .bind returns: a function reference.
.call and .apply return the return value of whatever function, in the new context. So in fake JavaScript la-la land, it would look something like this:
function() {
}.bind(this)
// returns a function reference: function() { }
Whereas:
function() {
return 'hello';
}.call(this)
// returns hello... NOT A FUNCTION REFERENCE!
You see...
You would probably never do something like this:
function hello() {
return true;
}
setTimeout(hello(), 100);
You'd get an error: setTimeout expected a Function, and it received a Boolean, or something like that.
^ That's not a very semantic error, but it's an error nonetheless.
But, what you would do is something like this:
function hello() {
return true;
}
setTimeout(hello, 100);
See the difference? That last example is okay, because you passed in a function reference.
Because internally, setTimeout does something like this:
window.setTimeout = function(callback) {
// a bunch of other stuff, and...
callback();
};
As far as your second question goes...
Those are pretty much all equivalent. Closures make sense though whenever you're declaring variables that you don't want to give other objects access to.
To help you understand closures a little bit, let's pretend we're not even talking about JavaScript.
In math, you know how you can do something like a + b * c?
Well, when you group them by parentheses, it kind of changes the behavior: (a + b) * c.
Now, obviously that's not quite related to JavaScript in the sense that in JavaScript we're not worried about order of operations (unless you're actually doing math in JavaScript), but the whole idea is that those parentheses just act as a container (what we call a closure) for whatever is inside of it.
So when you put a function inside parentheses, you're just hiding that function from the outside world, but it can still return stuff, and it can still have access to parent scopes (like window, for example).
A cool example:
var name = (function(name) {
return name
})(function() {
return 'Jane';
}());
console.log(name); // => Jane

Both .call and .apply execute the functions on which they are called immediately. What this means is that in those cases you are passing the result of call or apply on your anonymous function to setTimeout and that result is undefined because those functions don't return anything.
bind on the other hand returns a new function, which you are then correctly passing to setTimeout. To get call and apply to behave similarly, move them inside the anonymous function:
setTimeout(function(){
this.say.call (this, 'call');
}, 1000);
for example.
Afaik those are equivalent.

Related

What is going on inside the $.each()?

I am stumbling upon a problem that I have seen before, but that I couldn't solve before. I will likely stumble upon it again in the future, so please, someone explain it to me what is going on?
In the partial snippet of javascript below, I have a function that populates a screen, including an order combobox (twitter bootstrap). When I click on one of the order items in that combobox, it should invoke the function clsModCampaigns.blnCompaniesListReload().
For a reason that I don't understand, once inside the '$.each' iterator, the global object reference 'objModCampaigns' is lost? I get a successful alert '1', but not an alert '2'.
Within the $.each, I would like to use 'objModCampaigns.arrOrderBy' instead of 'this.arrOrderBy', but the $.each iterator only seems to work this way. Why is it working this way??
What is going on with 'this', or with variables/objects assigned in the root of the class with 'this'?
Is $.each just special??
function clsModCampaigns(objSetSystem, objSetModuleBase)
{
objModCampaigns = this;
arrOrderBy = {
intID: 'ID',
strName: 'Name'};
[...]
this.blnScreenCampaignInitialize = function (fncSuccess,fncError, intID) {
$.each(this.arrOrderBy, function (strFieldName, strFieldDescription) {
if(strFieldName != 'datDeleted' || objSystem.blnHasPerm("CAMPAIGNS_DELETED")) {
strOrderByID = "ulCampaignsCompaniesListOrderBy" + strFieldName;
$("#ulCampaignsCompaniesListOrderBy").append('<li>'+strFieldDescription+'</li>');
$("#"+strOrderByID).unbind("click").bind("click", function() {
alert("1");
objModCampaigns.arrCurrentShownCompanies.strOrderBy = strFieldName;
objModCampaigns.blnCompaniesListReload();
alert("2");
});
}
});
return true;
};
}
The code you have is
$.each(this.arrOrderBy, ...);
You want
$.each(arrOrderBy, ...);
The reason for it is the this context on that line is different because it is inside a new function this.blnScreenCampaignInitialize.
This is just a part of how JavaScript works
var message = "hello";
function welcome() {
console.log(message);
}
welcome(); // "hello"
P.S. use var
If you don't use var, you'll be attaching all of your vars to the global object.
function hello() {
foo = "bar";
console.log(foo);
};
hello(); // "bar"
console.log(foo); // "bar"
// Holy smokes! `foo` has escaped our `hello` function!
Compare that to
function hello() {
var foo = "bar";
console.log(foo);
}
hello(); // "bar"
console.log(foo); // ReferenceError: foo is not defined
// much better
Now let's see a terrible example
function a() {
b = 5;
return b;
}
function b() {
return "function";
}
console.log(a()); // 5
console.log(b()); // TypeError: number is not a function
This is happening because we didn't use var properly. We first define b as a function but after running a(), b is now set to 5. The second log statement is the equivalent of trying to run 5() because b is no longer a function.
P.P.S. it's pretty unconventional to prefix your vars with str, int, fnc, obj, or cls in JavaScript.
I understand you're a "VB guy" according to your comments, but that's no excuse for bringing your own conventions to the language. I see in your profile that you're fluent in Dutch, English, German, and French. I would recommend you treat learning programming languages much the same as spoken languages: each of them have their own explicit set of rules and conventions.
Here's a heap of free JavaScript books for you. I hope they can help you learn some more basics.
P.P.P.S. Overall, your function is really big as it is, and I can see you already truncated some of the code with your [...]. The whole thing could probably benefit from some better composition.
If you paste all of your code, maybe someone could help you better.
What is going on inside the $.each() ?
Regarding you question title, I'm trying to answer:
// each function in source
function (obj, callback, args) {
//...
}
Check the source of complete $.each function by yourself, you can see any function's source code just by typing the function name in the appropriate text box (the second on top).
Here in each function, the array/object passed in to the each function (the first argument) is being run through a loop and each value is being passed in to the callback (second argument) and that call back is getting executed like:
callback.apply(obj[i], args);
So, the passed callback in the each function is being executed each time the loop occurs ad the current value in the loop is passed as the argument of callback function along with the third argument args.
If your function function clsModCampaigns(){ //... } is a normal function then this inside this function points to global window object. so just use:
$.each(arrOrderBy, ...);
instead of
$.each(this.arrOrderBy, ...);
Because, arrOrderBy is within the direct scope so arrOrderBy is accessible directrly. For example:
function someThing()
{
var x = 'y'; //<-- this scope (everything inside someThing) is
// global for somethingElse inner function
function someThingElse)(x) //<-- possible to use directly
{
}
}
The keyword this behaves differently depending on the context. Check about this on MDN.

Confused about the 'this' keyword in javascript

I haven't used Javascript in a long time and have been refreshing myself on it today. One thing that always got me was the this keyword. I know in jQuery event handlers, such as the click event, this refers to the element that fired the event. How is this passed to the function that I give it as a callback even though my function has no arguments?
Given the following code:
$("tr.SummaryTbRow").data("Animating", false);
$("tr.SummaryTbAltRow").data("Animating", false);
$("tr.SummaryTbRow").click(function () {
if ($(this).data("Animating") == false) {
if ($(this).next(".Graph").css("display") == "none") {
$(this).data("Animating", true);
//Part I am questioning.
setTimeout(function () {
$(this).data("Animating", false);
}(this), 550);
$(this).next(".Graph").slideRow('down', 500);
}
else {
$(this).data("Animating", true);
$(this).next(".Graph").slideRow('up', 500);
}
}
});
I am trying to figure out how to pass the element table row with class SummaryTbRow to my setTimeout call back function. Does jQuery pass this in a similar fasion to what I am doing with my anonymous call back function? Does my this inside the function refer to the this I pass in?
I know I could just do:
setTimeout(function (element) {
$(element).data("Animating", false);
}(this), 550);
But I want to figure out how jQuery is able to pass this to my call back function even though my function takes 0 arguments.
To answer you last question, you may pass the receiver of your choice to a function in javascript using for exemple call :
someFunction.call(someObject);
Inside someFunction, this will be someObject.
In your case, what you seem to want is
setTimeout(function (element) {
$(element).data("Animating", false);
}, 550, this); // this will be passed as element to the callback
or (more compatible)
var _this = this;
setTimeout(function () {
$(_this).data("Animating", false);
}, 550);
Short answer:
You can set this on a function by using the function's .call() and .apply() methods.
Long answer:
The this variable on any function is similar to the arguments variable (which is probably something you didn't know about). It's set when the function is called and is an artifact of how the function is called. To explain, let me start with a demonstration of arguments. Consider:
myFunction = function () {
return arguments.length;
};
Now let's look at a couple calls to myFunction:
myFunction(); //is 0
myFunction(null); //is 1
myFunction(undefined); //is 1
myFunction(0, 0, 0, 0, 0); //is 5
As you can see, the value of arguments.length is not dependent on how or where we wrote the function, but on how we called the function. The same is true of the this variable (otherwise known as the "calling object"). There are exactly 3 methods for setting the calling object (there's a sort-of 4th in ES5, but we'll ignore that):
You can set it by calling the function using dot-notation (e.g. something.myFunction())
You can set it by using the function's .call() or .apply() methods (e.g. myFunction.call(someObject))
If it's not set using method #1 or #2, it will default to the global object (e.g. window)
So most people are used to method #1. If you assign your function as a property of an object, then call the function using the object and dot-notation, then the object gets set as this. Like so:
var myFn = (function () { return this.x });
var myObj = {
x: 1,
y: myFn
};
myObj.myFn(); //is 1
But we can also use method 2 if myFn isn't a property of the object we want to call it on but the object follows the correct form for myFn to be able to act on it (see: duck typing):
var myOtherObj = {
x: 2
}
myFn.call(myOtherObj); //is 2
myFn.apply(myOtherObj); //is 2
myFn.apply({ x : 3}); //is 3
Pretty cool, eh? This is how jQuery does it. When they execute your callback, they do so using .apply(event.target) (setting this to the event's target object). They do it in a more complex manner because of their callbacks framework, but the idea is there.
Anyway, I wouldn't be offering a long answer if I didn't toss in an explanation of method #3, which drives some people totally insane: What happens if you don't set the calling object?
Because all global variables are implicit properties of the global object, you get some interesting effects from method #3. Such as:
var x = 4;
myFn(); //is 4
But most of the time you're not lucky enough to have your global object meet the requirements the function has for its calling object, so usually it just results in an error and a lot of frustration.
Probably more than you were looking for, but hopefully you're now much more informed on the calling object and its wily ways.
You can call a function using fn.call or fn.apply both of which take a context argument which is used for this.
Apply, call and bind methods serve for that purpose. In your case you just write:
setTimeout(function() {
$(this).data("Animating", false);
}.bind(this), 550);

Why doesn't this extension work?

I wanted to have a method of adding functionality to pre-existing functions, so I made this:
Function.prototype.attachFunction = function(toAttach)
{
var func = this; //This is the original function, which I want to attack toAttach function to.
//Can I do this without wrapping it in a self-executing function? What's the difference?
func = (function()
{
return function()
{
func();
toAttach();
}
})();
return func; //Return the newly made function, not really important.
}
I paste this into the Google Chrome console, and there are no errors, however, it does not (or so it would seem) alter the original function at all.
f = function() {console.log("g");};
f.attachFunction(function(){console.log("New function!");});
f(); //Prints out just "g".
When attachFunction executes, it returns a function which executes func() and toAttach. However, if you change your code to be the following, it will attempt to execute both functions, where currently the old f is still called at the end.
f = function() {console.log("g");};
f = f.attachFunction(function(){console.log("New function!");});
f(); //Causes an infinite loop since calling func() is the same as calling this()
To merge two functions we need to wrap them the same way but not while extending Function
var func1 = function(){
console.log('g');
};
var func2 = function(){
console.log('New function!');
};
func1 = (function(f1,f2){
return function(){
f1();
f2();
}
}(func1, func2));
func1(); //Writes out both g and New function!​​​​​​​​​​​​​​​​​​​​
The reason I pass in func1, and func2 as parameters is to prevent the same problem of getting into an infinite loop. I want to maintain a reference to these functions at that point in time.
A helper function would then be the following
function combineFunctions(f1, f2){
return function(){
f1();
f2();
}
}
and would be used like this
var f = function() {console.log("g");};
f = combineFunctions(f,function(){console.log("New function!");});
f();
The attachFunction method you've set on the Function prototype is a higher-order function, or combinator, that accepts a function and returns a new function. It is inherited by any function created thereafter, as expected. However, when it is called it does not alter the state of f in any way, it simply produces a new function that calls f, or the calling function rather.
So, if you wanted to see both console.log messages, you simple need to call the function it returns, like so:
f.attachFunction(function(){console.log("hi")})();
or more simply:
f.attachFunction(f)();
Note that although functions are objects, the execution context (code) of f is not a property of f such that it can be manipulated directly, it is kept in an internal attribute.
Underscore.js' wrap() function should be quite close to what you want.
Currently, you're trying to call a variable as if it were a function (toAttach is a variable and you're smacking some parenthesis on the far side of it). Consider this: toAttach may CONTAIN a function, but that does not make it a function (much like a grocery store is not an apple). Instead you need to call the function contained within the variable.
There are several ways to do this (although only one or two apply here), none of the [right] ways involve eval() (life lessons).
Consider this thread. Pass the function name as a string and then use
window[toAttach]();
to call the function. It's a bit hacky, and lacks the finesse of passing the function as an object, but it works.
Alternatively (completely depending on your needs) you can bind the function to an event. If you're extra sneaky (and why not?) you can use
setTimeout(toAttach, 0);
Where toAttach is a reference to the actual function (again).
Finally, and best, I believe, is the Function.call() method. Using this, you can call a function stuffed in a variable, and even pass it a context for this.
toAttach.call([Context]);
Where [Context] is the context of the function (ie, what this refers to when inside of the function, in most cases it can be left blank to get the desired scope). I can't say I've tested it under your certain circumstances (thus why I have included it last), but I believe it should give you the results you're looking for.
Here's my attempt to achieve this, it is not clean as if it was entirely in the prototype but it works. There are probably better ways than mine.
var bindings={};
function execBindings(func_name) {
if (bindings[func_name] != undefined) {
for (var i=0;i<bindings[func_name].length;i++) {
bindings[func_name][i]();
}
}
}
Function.prototype.bind=function(action){
if (bindings[this.name] == undefined) {
bindings[this.name]=[];
}
bindings[this.name].push(action);
}
Then, to use the binding :
function f() {
alert("foo");
execBindings("f");
}
f.bind(function(){
alert("bar");
});
f.bind(function(){
alert("test");
});
f();
which results in 3 alerts, in the order thwy were binded to the function.
There are two problems I just found.
problem 1.
f = function() {console.log("g");};
f.attachFunction(function(){console.log("New function!");});
f();
It definitely doesn't work, cause attachFunction just wrap around your original function and return the wrapped, it doesn't change the original function, which means your f function is still f = function() {console.log("g");}
Problem 2.
If you reassign f with the result of attachFunction, an Maximum call stack size exceeded will occur. In the definition of attachFunction:
func = (function()
{
return function()
{
func();
toAttach();
}
})();
return func;
The func function call himself recursively.
Update
I prefer this approach.
Function.prototype.attachFunction = function(toAttach)
{
var that = this;
func = (function()
{
return function()
{
that();
toAttach();
}
})();
return func; //Return the newly made function, not really important.
}
f = function() {console.log("g");};
f = f.attachFunction(function(){console.log("New function!");});
f();

What's the meaning of "()" in a function call?

Now, I usually call a function (that requires no arguments) with () like this:
myFunction(); //there's empty parens
Except in jQuery calls where I can get away with:
$('#foo').bind('click', myFunction); //no parens
Fine. But recently I saw this comment here on SO:
"Consider using setTimeout(monitor, 100); instead of setTimeout('monitor()', 100);. Eval is evil :)"
Yikes! Are we really eval()-ing a string here? I guess I don't really understand the significance and implications of 'calling' a function. What are the real rules about calling and referring to functions?
In JavaScript functions are first-class objects. That means you can pass functions around as parameters to a function, or treat them as variables in general.
Let's say we are talking about a function hello,
function hello() {
alert('yo');
}
When we simply write
hello
we are referring to the function which doesn't execute it's contents. But when we add the parens () after the function name,
hello()
then we are actually calling the function which will alert "yo" on the screen.
The bind method in jQuery accepts the type of event (string) and a function as its arguments. In your example, you are passing the type - "click" and the actual function as an argument.
Have you seen Inception? Consider this contrived example which might make things clearer. Since functions are first-class objects in JavaScript, we can pass and return a function from within a function. So let's create a function that returns a function when invoked, and the returned function also returns another function when invoked.
function reality() {
return function() {
return function() {
alert('in a Limbo');
}
};
}
Here reality is a function, reality() is a function, and reality()() is a function as well. However reality()()() is not a function, but simply undefined as we are not returning a function (we aren't returning anything) from the innermost function.
So for the reality function example, you could have passed any of the following to jQuery's bind.
$('#foo').bind('click', reality);
$('#foo').bind('click', reality());
$('#foo').bind('click', reality()());
Your jQuery bind example is similar to setTimeout(monitor, 100);, you are passing a reference of a function object as an argument.
Passing a string to the setTimeout/setInterval methods should be avoided for the same reasons you should avoid eval and the Function constructor when it is unnecessary.
The code passed as a string will be evaluated and run in the global execution context, which can give you "scope issues", consider the following example:
// a global function
var f = function () {
alert('global');
};
(function () {
// a local function
var f = function() {
alert('local');
};
setTimeout('f()', 100); // will alert "global"
setTimeout(f, 100); // will alert "local"
})();
The first setTimeout call in the above example, will execute the global f function, because the evaluated code has no access to the local lexical scope of the anonymous function.
If you pass the reference of a function object to the setTimeout method -like in the second setTimeout call- the exact same function you refer in the current scope will be executed.
You are not doing the same thing in your jQuery example as in the second setTimeout example - in your code you are passing the function and binding the click event.
In the first setTimout example, the monitor function is passed in and can be invoked directly, in the second, the sting monitor() is passed in and needs to be evaled.
When passing a function around, you use the function name. When invoking it, you need to use the ().
Eval will invoke what is passed in, so a () is required for a successful function invocation.
First of all, "()" is not part of the function name.
It is syntax used to make function calls.
First, you bind a function to an identifier name by either using a function declaration:
function x() {
return "blah";
}
... or by using a function expression:
var x = function() {
return "blah";
};
Now, whenever you want to run this function, you use the parens:
x();
The setTimeout function accepts both and identifier to a function, or a string as the first argument...
setTimeout(x, 1000);
setTimeout("x()", 1000);
If you supply an identifier, then it will get called as a function.
If you supply an string, than it will be evaluated (executed).
The first method (supplying an identifier) is preferred ...

callback functions

Man Im trying to understand callback functions. Ive been over many articles and posts here on SO. The explanations seem circular and I think Im actually getting farther from understanding lol. Ive used them apparently in javascript events, but its more a 'memorize these lines' than 'this is whats going on and why' sort of understanding.
So heres my understanding.
Say you have 2 objects, function p() and function k(). You pass function k to p(). p() can then access k's inner variables.
function p(x){
alert(x.n);//5
}
function k(){
this.n = 5;
}
p(k);
Embarrassing how long its taken me to get just this.
Maybe an example will help?
// First, lets declare the function we're going to call
calledFunction = function (callback, arg) {
callback(arg);
};
// Second, lets declare the callback function
callbackFunction = function (arg) {
alert(arg);
};
// Next, lets do a function call!
calledFunction(callbackFunction, "HAI");
So, calledFunction()'s callback argument is callbackFunction but, if you notice, we aren't calling the function yet, we're passing a variable the contains the function, and its arg function is just something to alert(). When calledFunction() is executed it takes whatever was passed as the callback argument and calls it with arg as its first, and only, argument.
Helped?
Edit: This still works if you use function foo() {}-style declarations. (just in case; I don't know how fluent you are with JavaScript)
You are doing it wrong. this.n = 5; in k() does not set its "inner variable", and x.n access the function object's x property, instead of its inner variable.
Try this:
function p(x) { alert(new x().n); }
Variable binding is an important programming concept.
I think this article helps. http://www.hunlock.com/blogs/Functional_Javascript

Categories