In callback function with Javascript closure, how to access the global variable - javascript

It is simple:
I need to register an event property with a function. But in order to pass arguments to the function, I create a closure.
var x=0
function foo(bar1,bar2){
return function (){
alert (bar1+bar2);
x++;
}
}
for (var i=0;i<document.getElementsByTagName("div").length;i++) {
document.getElementsByTagName("div")[i].onclick=foo(x,i)
}
Since I have 5 div elements, and I thought it should alert like this if I click all the div from top to down:
0
2
4
6
8
but instead it output:
0
1
2
3
4
It seems like that every time in foo(x,i), x is equal to 0. How do I get foo() to access the new value of x instead of its first initiation?

You are incrementing x only within the inner function. This function is only called once an element is clicked. As a result, at the time foo is called, x is always 0. It is only incremented later when something is clicked and at that point the values of bar1 are already set (to 0).
You could do something like this instead:
var x=0
function foo(bar1){
return function (){
alert (bar1+x);
x++;
}
}
for (var i=0;i<document.getElementsByTagName("div").length;i++) {
document.getElementsByTagName("div")[i].onclick=foo(i)
}
In this way you will always use the current value of x instead of the value at the time foo was called.

You need to increment x where you increment i. Because on every iteration x is still zero thus the values you're getting 0,1,2,3,4 are actually the values of i.
In your code, you are passing the value 0 to the variable bar1 on every iteration and the only time that x increments is after you click the button not during the iteration, so if you put another alert for x in front of this alert alert (bar1+bar2); you see what i mean.
Finally to increase x on every iteration you must explicitly do it in the loop. like this:
;i++, x++)
JSFIDDLE
This would output 0,2,4,6,8 without increment x on every click.
var x = 0,
div = document.getElementsByTagName("div");
function foo(b1, b2){
return function () {
alert (b1 + b2);
}
}
for (var i = 0; i< div.length; i++, x++) {
div[i].onclick = foo(x, i);
}

Related

How does JavaScript "save" values via variables?

**Have updated to put count and function in.
My question is that do equations when the return expression is put in, save that value so if that function is called again, it saves the previous value and adds on top of it? And I guess can someone elaborate on a higher level why/how this is?
(i.e.: console.log ((cc(3)) = 1 Bet)); console.log (cc(2)) = 2 Bet, instead of 1 Bet (count from cc(3) was "saved"))
var count = 0;
function cc(card) {
// Only change code below this line
if (card>=2 && card<=6) {
count+= 1;
} else if(card>=7 && card<=9) {
} else {
count-=1;
}
if (count>0) {
return count + " Bet";
} else {
return count + " Hold";
}
}
My question is that do equations when the return expression is put in,
save that value so if that function is called again, it saves the
previous value and adds on top of it?
No. Equations don't save anything by themselves.
Variables declared within a scope retain their value for some period of time (depending upon the scope they are declared in and whether they are part of any sort of closure). So an equation that references a variable will always use the latest value of that variable. And a function that modifies a variable that is later used in an equation will cause the equation to see a new updated value.
EDIT
Since you now show that card is a variable in a higher scope that your cc() function, we can now explain that card will retain its value from one call to cc() to the next.
End of Edit
In the code you show, the current value of card is used anytime your code runs. You don't show where that variable is declared or how its value is set so we can't comment on that. When your code runs, it will use the current value of the card variable.
Likewise in the code you show, the current value of count will be used, updated and returned. Whether that modified value remains for the next time this code is run depends entirely upon how/where count is declared. You would need to show us more of your code including where count and card are declared and where else their values are set for us to comment further on exactly what happens with them.
Argument to a Function
If you do this:
function increment(item) {
return ++item;
}
var cntr = 0;
console.log(increment(cntr));
console.log(increment(cntr));
You will get output that looks like this:
1
1
Because the value of cntr was never changed so each time you call increment() you were essentially doing increment(0) and nothing ever modifies the value of cntr so it stays at 0.
Assign Back Result to Original Variable
If, on the other hand, you did this:
function increment(item) {
return ++item;
}
var cntr = 0;
cntr = increment(cntr);
console.log(cntr);
cntr = increment(cntr);
console.log(cntr);
Then, you would see this:
1
2
That's because you are assigning the return value from the function back to cntr so it's value is updated each time. So, the first time you call increment(), your doing increment(0) and the next time you're doing increment(1).
Local Variables
Local variables within a function exist only for the scope of that function. So, if you did this:
function increment() {
var val = 0;
return ++val;
}
console.log(increment());
console.log(increment());
You would see this:
1
1
Each invocation of increment() creates a new local variable val and initializes it to 0. So, everytime you call increment it is just going to get the value of 0 and increment it by one and return that so it will always return 1.
Higher Scope Variables
If, however, the variable val was in a higher scope:
var val = 0;
function increment() {
return ++val;
}
console.log(increment());
console.log(increment());
Then, you would see this:
1
2
That's because the variable val is in a higher scope and the same val variable exists for both of your calls to increment() so it's value is carried from one call to the next.
Because you should declared the count variable outside of the function, not inside the function.If you want to print as you desire, put the count variable inside the function.
Try it :
<html>
<head>
<meta charset="utf-8">
<style>
</style>
</head>
<body>
<script>
function cc(card) {
var count = 0;
if (card>=2 && card<=6) {
count+= 1;
} else if(card>=7 && card<=9) {
} else {
count-=1;
}
if (count>0) {
return count + " Bet";
} else {
return count + " Hold";
}
}
console.log(cc(3));
console.log(cc(2));
</script>
</body>
</html>
I think you want to use a closure to store a variable that lasts across calls.
var cc = (function (){
var count = 0;
return function(value) {
count += value;
return count;
}
})();
cc(2) // 2
cc(3) // 5
This is also known as the module pattern, returning a function instead of an object.
The easy way out would be to make count global, but doing it with the iife makes it so that no other code has access to that variable

How to permanently change var?

So, I have a var set in a function and a array(called "card_idx) set up, and I want the var be set to 0 until a certain number is reached in the array but the number doesn't go up in order (1..2..3..4 extra). It jumps around depending on how the person plays ( so it can be like...1...2...2.1....5....3.2...). And I want the var to be set to 0 until a specific number is reached and then it is changed to 1.
I try having it set up like:
var x=0;
if(card_idx == 3.2){
x=1
}
but the moment there no longer on 3.2 it will change back to zero, how do i make it so it will stay 1?
While your example isn't complete enough to reproduce the problem, I imagine you may be running into trouble with variable scope.
JS variables are locally scoped to the function surrounding them, which works to your advantage here. If you declare x at the beginning of the function that goes through your data, the loop can modify it and the value will be retained after the loop completes:
function crunch(data) {
var x = 0;
data.forEach(function (item) {
if (item.index === 3.2) {
x = 1;
}
});
console.log(x);
}
If any item in data had an index of 3.2, x will be set to 1 and printed to the console at the end. The callback to forEach grabs x using closure, but this would work just the same with a for loop.
Using x within the loop, the value will not be reset until crunch returns. Every time crunch is called, x will be set to 0, may be set to 1 if an item has the right index, and will retain that value until the end of crunch.
Now, with forEach, if you were to declare x inside the loop callback rather than in crunch, it would reset every time:
function crunch(data) {
data.forEach(function (item) {
var x = 0;
if (item.index === 3.2) {
x = 1;
}
});
}
Because var operates at the function level, this will not keep its value and will be 0 for every item.
You could try this. Use an extra boolean to check if x has ever been set.
Be aware that your variables are outside the iteration.
var x = 0;
var hasSet = false;
// start looping
if (card_idx == 3.2 && hasSet = false) {
x = 1
hasSet = true;
}
Or maybe (if your question was more clear) this will work out too.
var x = 0;
// start looping
if (card_idx == 3.2 && x <= 0) {
x = 1
}

Why JavaScript alerts allways 4 in when for-loop is for 3?

Why this JavaScript alerts allways 4 in when for-loop is for 3?
// alert
function reOrderLayers(layerToaAlter) {
alert(layerToaAlter);
}
// prepare
var laCount;
for (laCount = 1; laCount <= 3; laCount++) {
var la = document.getElementById("layerChanger"+laCount);
la.addEventListener("click", function () { reOrderLayers(laCount) });
}
When loop is finished means i is >3, to preserve particular number in function call use closures:
la.addEventListener("click", (function(count) { return function () { reOrderLayers(count) } })(laCount) );
The for loop continues until laCount == 4 (the last time the loop is executed the value is 3 and then it is incremented to 4 so that the loop test fails). Therefore the value is 4 after the for loop.
You didn't call reOrderLayers immediately in the loop but constructed a function literal. The literal will have a closure and it refers to the laCount variable in the environment which will be 4 when the click event is eventually fired.
Because when you click on the element; for loop is already finished and current value of laCount would be 4;
you should create a context to save laCount for each layerChanger:
function reOrderLayers(layerToaAlter) {
alert(layerToaAlter);
}
function context(la, laCount){
la.addEventListener("click", function () { reOrderLayers(laCount) });
}
// prepare
var laCount, la;
for (laCount = 1; laCount <= 3; laCount++) {
la = document.getElementById("layerChanger"+laCount);
context(la, laCount);
}
Because:
var c = 0;
for(c = 1; c <= 3; c++) {};
alert(c); // alerts 4.
During the first 3 iterations the for loop goes into the body and conducts the code you have inside. Once you're on the 4th iteration, lacount get incremented and the condition fails, so that you don't go into the loop again.
If you needed lacount to be 3 for some reason, you could try setting the initial for loop condition to i < 3.

Reusing the same shortcut.js function to handle keyboard input

I'm using shortcut.js to handle keyboard input and I'm wondering if there is a more efficient way to achieve my goal (currently most of the same code is copied and pasted).
For example, i have:
shortcut.add("0",function() {
points = -1;
sec = 0;
});
shortcut.add("1",function() {
points = 1;
sec = 0;
});
shortcut.add("2",function() {
points = 2;
sec = 0;
});
shortcut.add("3",function() {
points = 3;
sec = 0;
});
Ideally, I can generalize the function so that whatever key is entered is actually assigned to the points variable, except in the case where the user enters 0. In that case, the points variable is set to -1.
Any ideas on how to make this happen? Thank you!
A loop with a closure should do the trick:
for (var i = 0; i <= 9; ++i) {
(function(i) { // Capture current value of 'i' in this scope.
shortcut.add(i.toString(), function() {
points = i || -1; // 'i' if 'i' is not 0, else -1.
sec = 0;
});
})(i);
}
Update following comment: So why do we need a closure here? And what does the final (i); mean?
Basically, we need a closure because the anonymous functions passed to shortcut.add() will not be called right away, but some time in the future, after the loop has terminated. The functions capture i by reference, not by value, which means they will see the value of i that is current at the time they run, not at the time they're defined.
So, if we call shortcut.add() directly from the loop body, all the anonymous functions we pass will end up seeing the value of i that is current after the loop has terminated, which will always be the same (10).
Creating a new variable in each iteration looks like it could work, but doesn't:
for (var i = 0; i <= 9; ++i) {
var _i = i; // Create new variable containing current value of 'i'.
shortcut.add(i.toString(), function() {
points = _i || -1; // Won't work, '_i' is always 9.
sec = 0;
});
}
Since for loop bodies do not have their own scope in Javascript, _i ends up in function scope, the same as i, and will be captured the same way (its final value will be 9 instead of 10 because ++i does not apply to it).
So, what we really need here is a new scope in each iteration. To achieve this, we can define a function inside the loop, and call it immediately, passing it the current value of i:
var newScope = function(i) {
// Here, the value of 'i' will be the one current when 'newScope' is called
// and will not change, even if 'i' is captured by other functions.
};
newScope(i); // Call function with current value of 'i'.
Finally, we can do that without introducing the newScope name, by directly applying the call operator () to the function definition:
(function(i) {
// Here, the value of 'i' will be the one current when this function is
// called and will not change, even if 'i' is captured by other functions.
})(i); // Call function with current value of 'i'.
I hope this appropriately answers your questions, feel free to leave further comments if it does not. For more information about closures, see Closures on MDN.

JavaScript Broken Closures and Wrapper Function from John Resig #62 and #63

The first example below is #62 from John Resign`s Learning Advanced JavaScript http://ejohn.org/apps/learn/#62. It is called Fix the Broken Closures. Example 1 fails 4 times. Example 2, which is only different because it has a wrapper function, passes 4 times. It is example #63 from the same tutorial
Can someone please explain
1) why i == count++ in example 1 fails.
2) why i == count++ passes with the help of the wrapper function. How does the wrapper function change things to make it work?
Thanks in advance.
Example 1
var count = 0;
for ( var i = 0; i < 4; i++ ) {
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
}
Example 2
var count = 0;
for ( var i = 0; i < 4; i++ ) (function(i){
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
})(i);
This is pretty straight forward.
Since setTimeout executes "asynchronously", there is no way of telling the exact value of i when the function executes, since the loop carries on running.
By using a function wrapper, effectively you are treating the body of the call as a function and are EXPLICITLY passing in the value of i.
You could clear this up by renaming the function i param to j or something else and update the innards of the function to from i to j
Basically it boils down to scoping
Since i is being incremented in the loop the odds are strong that each time the setTimeout callback is invoked the value of i will be 4.
The wrapper function introduces a new scope allowing the value of the parameter i to maintain its value even though the surrounding i is being incremented by the loop.
function outerScope() {
var x = 2, y = 3;
function innerScope() {
var x = 3;
// Obviously this alerts 3.
alert(x);
// Since we have no 'y' defined, alert the value 3 from the outer scope.
alert(y);
}
// Introduce a new scope.
innerScope();
// Since we have left the inner scope x is now 2.
alert(x);
// Obviously this alerts 3.
alert(y);
}

Categories