setTimeout within function fails - javascript

This function accepts an argument, whichImage. This is the object HTMLImageElement for the image we are working with. The image width will be halved, and then after 3 seconds, it will return to normal width. However, the setTimeout code, which is supposed to execute after 3 seconds, fails, with the error message that whichImage is not defined. What do I need to correct to make this function work?
function resizeImageFunction(whichImage){
// Get the width of the image
alert(whichImage);
var width = whichImage.width;
var halfwidth = Math.round(width/2);
whichImage.width=halfwidth;
setTimeout("whichImage.width=width;",3000);
}

function resizeImageFunction(whichImage){
// Get the width of the image
alert(whichImage);
var width = whichImage.width;
var halfwidth = Math.round(width/2);
whichImage.width=halfwidth;
setTimeout(function(){
whichImage.width=width;
},3000);
}

The explanation for your problem is as follows:
When you pass a string to setTimeout(), that string will be evaluated by eval() in the global scope. Thus any function you call or variable you reference there must be available from the global scope. That explains why you can't reference a local variable or an argument to your function because neither or those are in the global scope so when eval() tries to find them, it looks in the global scope and they aren't there.
When you change the setTimeout() function to this using an inline anonymous function:
setTimeout(function() {
whichImage.width = width;
}, 3000);
now you have real javascript code (not a string) that is evaluated in place where it exists with no use of eval() and because of closures you have full access to the local variables and arguments of the enclosing function which gives you access to both whichImage (an argument) and width (a local variable) so your code works.
This is reason #14 which you should always use real javascript function references or anonymous function declarations instead of passing a string to setTimeout().

You need to wrap your block of code in an anonymous function like:
setTimeout(function () {
whichImage.width=width;
}, 3000);

Try:
setTimeout(function() {
whichImage.width=width;
},3000);

You don't need to use eval for this
setTimeout(function() { whichImage.width=width; } ,3000);
Here is is your function
function resizeImageFunction(whichImage){
var halfwidth = Math.round(width/2);
whichImage.width=halfwidth;
setTimeout(function() { whichImage.width=width; } ,3000);
}

Related

If anonymous JavaScript functions can access all variables, what about regular functions?

In Javascript function variables are local unless they're not declared with "var". That is, unless those functions are anonymous (like the ones used in setInterval and setTimeout. In that case, they have global access to all available variables within the scope that called the anonymous functions.
What about regular functions then? Is there any way to allow them to access all variables too? I ask this due to some cases of having very complicated functions under 1 parent function with many variables, where I suddenly have to turn them from anonymous into regular functions.
For example, let's assume my original anonymous function called alert and now I want to do it through a regular function:
myFunction()
function myFunction() {
var a='b' //,b=...,c=...,d=...,e=...,f=...,g=...,h=...,i=...,j=...,k=...
setInterval(function(){ alert(a) }, 3000) // Works even though a is local
setInterval(function(){ dummy() }, 3000) // Fails, but would work if I used dummy(a).
// But then I'd have to use dummy(a,b,c,d,e,f,g,h,i,j,k,...)
}
function dummy() { // It would have worked had I used dummy(a)
alert(a)
}
The question is not about anonymous functions. Take this two lines:
setInterval(function named (){ alert(a) }, 3000);
setInterval(function(){ alert(a) }, 3000);
both work although one is named, and one is not. The real problem here is scoping, a is not visible to dummy because dummy gets declared outside of a's scope:
{ // brackets start a scope
let a = "b"; // a gets declared in this scope
//...
} // scope ends
function dummy() { /*..*/ } // outside of scope
To solve this, move dummy into a's scope:
function myFunction() { // start of scope
let a = "b";
setInterval(function(){ dummy() }, 3000);
function dummy() {
alert(a); // a can be accessed as it is inside of the scope
}
} // end of scope
(I used let instead of var because it has stricter, and therefore easier to justify, scoping rules)

JavaScript anonymous function expression vs IIFE

Encountered some code that's using IIFEs in an expression rather than just a normal function.
var custom_type = (function() {
return $('#myDiv').attr('custom_type');
})();
Normally I would write this as something like:
var custom_type = function() {
return $('#myDiv').attr('custom_type');
};
What's the reason for the IIFE? The only thing I can think of is that the IIFE might assign the custom_type variable only once at the beginning whereas the second might continue to check for the updated type every time the variable is referenced.
In this example, you can dispense with the function altogether and just do:
var custom_type = $('#myDiv').attr('custom_type');
However in general you can use an IIFE for more complex "just-in-time" computation of variable assignments - I like to use them if I need to iterate over something, so I can have i without polluting the current scope.
In your second example, though, the result is completely different - you will need to call the function custom_type() to get the current value, whereas the first piece of code will get its value once, and the variable will hold that value.
The IIFE will actually run (immediately-invoked function expression), so the variable will be set to its response.
Here's a JSFiddle to demonstrate this, watch your JS console for the output: http://jsfiddle.net/Y4JmT/1/
Code Here:
var custom_type = (function() {
return 'foo';
})();
var custom_type2 = function() {
return 'bar';
};
console.log('iife = ' + custom_type);
console.log('non-iife = ' + custom_type2);
In your JS console you'll see something similar to:
iife = foo
and
non-iife = function () {
return 'bar';
}
The first one of yours (IIFE) executes the function and store its result, the seconds stores function as definition.
(function(x) {
return x * 2;
})(5);
You are making a call like to normal funciton: func(arg1, arg2), but instead of function name you pass whole function definition, so the above would return 10 like:
function multiply(x) {
return x * 2;
}
multiply(5);
They mean the same thing, you are making calls. Unlikely the first, second is definition plus a call.
IIFEs allow you to invoke a function (anonymous or otherwise) at the point of creation.
Have you looked at this?
http://benalman.com/news/2010/11/immediately-invoked-function-expression/

setTimeout giving arguments to function from another function

Got a problem, don't know how can I give to ClickSimClick function arguments exposed by ClickSimMove func (it returns array with 2 values).
Code below says that crd is undefined on setTimeout.
var crd = plugin().ClickSimMove();
setTimeout("plugin().ClickSimClick(crd[0], crd[1])", 1000);
Pass a function, not a string:
var crd = plugin().ClickSimMove();
setTimeout(function() {
plugin().ClickSimClick(crd[0], crd[1]);
}, 1000);
When you pass a string, it's evaluated as it would be with eval in the global scope, losing all access to local variables. An anonymous function lets you reference any variable in scope.
var crd = plugin().ClickSimMove();
setTimeout(function(){
plugin().ClickSimClick(crd[0], crd[1]);
}, 1e3);
When at all possible, avoid sending strings to setTimeout/setInterval--use an anonymous function instead. Especially if you find yourself concatenating variables to make that string, you can run in to trouble very quickly with some sort of injection or malformed component.

Why won't my timer increment the number?

I recently learned javascript. I was experimenting with it. Now, I tried to make a simple timer. Here is the code:
<html>
<head>
<script type="text/javascript">
function start(obj)
{
var t = setTimeout("increment(obj)", 1000);
}
function increment(obj)
{
obj.innerHTML = parseInt(obj.innerHTML) + 1;
start(obj);
}
</script>
</head>
<body>
<p onclick="start(this)">0</p>
</body>
</html>
The contents of the <p></p> should be incremented by 1 every second. Does anyone know why this doesn't work?
Because the string you pass into setTimeout is evaluated at global scope, not the scope within the function, and so doesn't refer to your obj object.
You should never pass strings to setTimeout. Instead, pass it a function reference:
function start(obj)
{
var t = setTimeout(function() {
increment(obj);
}, 1000);
}
function increment(obj)
{
obj.innerHTML = parseInt(obj.innerHTML) + 1;
start(obj);
}
The function we're passing to setTimeout is a closure, which means it has an enduring reference to the items in scope where it's defined. So a second later when the timer mechanism calls it, it still has access to the obj argument of your start function even though the start function has long since returned. More here: Closures are not complicated
The issue (or at least, the first that I see) is that you are passing the string "increment(obj)" to the setTimeout() method, but obj is only defined inside of your start() method. The string that you pass isn't actually evaluated until the timeout triggers, at which point no obj variable is in scope.
There are a few different ways around this. One is to pass a closure to setTimeout() instead of a JavaScript string, like:
function start(obj) {
var nextIncrement = function() {
increment(obj);
};
var t = setTimeout(nextIncrement, 1000);
}
Another (though less preferable) option is to promote obj to the global scope, like:
function start(obj) {
window.obj = obj;
var t = setTimeout("increment(obj)", 1000);
}
In general, however, you should avoid passing a string to setTimeout (and you should also avoid placing things in the global scope unnecessarily). As you have seen, it can cause issues with scope resolution, and for any non-trivial operation it also makes you code much less maintainable. Always prefer passing a function closure when possible.
Also, the following line of code is probably not doing exactly what you expect:
parseInt(obj.innerHTML)
You should always provide the radix argument to parseInt, to avoid errors with values such as 011 (which is 9, rather than 11, because it is evaluated in base-8 due to the leading 0). You can avoid these quirks by simply doing:
parseInt(obj.innerHTML, 10)
...to force a base-10 parse.
Anyways, working example here: http://jsfiddle.net/dSLZG/1
The problem is with this line of code:
var t = setTimeout("increment(obj)", 1000);
obj is an identifier in the functions scope -- it is only accessible within the start function. When you pass a string to setTimeout, it is evaluated in the global scope. This means that the obj variable is not available, so nothing is incremented.
You should pass a function object instead, as this will create a closure and your variable will be accessible:
function start(obj)
{
setTimeout(function() {
increment(obj);
}, 1000);
}
Note that I have removed the unnecessary var t =, because you're not doing anything with the timer identifier.
I've copied your code over to jsFidle and added a working example. See sample code.
The problem in your original version is that your variable obj is not defined in the global context. Whenever you pass strings to setTimeout you'll end up having the string evaluated in the global context (where obj is not a variable).
What you should do instead is never pass a string to setTimeout but a function object. This function object holds references to all variables that are set where the function object was defined.
So, because the function object passed to setTimeout in start2 is defined within start2 it has access to all variables and parameters of start2. Your variable obj is one of those and thus accessible within the function object as soon as it is executed by setTimeout.

Different behaviour between inline and non-inline JavaScript

I am trying to build a timer. Please compare the two situations (the first one works, not the second):
inline javascript http://jsfiddle.net/x7xhA/
non-inline javascript http://jsfiddle.net/x7xhA/1/
What is the problem?
This is a commonly encountered problem with users of jsFiddle's 'JavaScript section'. You see, the code that's put into the 'JavaScript section' is wrapped within a function used as a load handler, so in your second example, the real output result is this:
<script type='text/javascript'>
//<![CDATA[
$(window).load(function(){
var seconds = 0;
function timedCount() {
$("#txt").val(seconds);
seconds += 1;
setTimeout("timedCount()",1000);
}
});
//]]>
</script>
Now, timedCount isn't a global function anymore, as it's available in the scope of the load handler only, and when you use setTimeout with a string of code, this gets evaluated from the global scope.
Ways to fix this include:
change the setTimeout call to setTimeout(timedCount, 1000);
What this does, is passes the actual function object to setTimeout. Rather than evaluate the string of code, from global scope, each time, this essentially preserves the ability to call the function as scope doesn't matter anymore - you're handing the function to setTimeout.
var seconds = 0;
function timedCount() {
$("#txt").val(seconds);
seconds += 1;
setTimeout(timedCount,1000);
}
make timedCount a global function using timedCount = function() { ... };
This merely makes timedCount a global, so that when setTimeout tries to evaluate timedCount(); from the global scope, it succeeds as there is a timedCount function in the global scope.
var seconds = 0;
timedCount = function() {
$("#txt").val(seconds);
seconds += 1;
setTimeout("timedCount();",1000);
}
The second one wraps the timedCount function in jQuery ready function, hence is not available in global scope.
Fixed: http://jsfiddle.net/x7xhA/2/

Categories

Resources