How does JavaScript "save" values via variables? - javascript

**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

Related

Javascript event listener structure

I’m working on a code to get the index of a clicked element so it can add or remove a class to display or hide the information. For it I used for for iteration. But I don’t understand why is there an (i) after the event handler. I’m kind a newbie to coding so I want to understand everything.
Here’s the JavaScript code:
for (let i = 0; i < questions.length; i++) {
questions[i].addEventListener(‘click’,((e) => {
return function() {
if (clic[e].classList.contains(‘q-answered)) {
clic[e].classList.replace(‘q-answered’, ‘q-answeredno’);
} else if (clic[e].classList.contains(‘q-answeredno’)) {
clic[e].classList.replace(‘q-answeredno’, ‘q-answered’);
}
}
})(i))
}
Let's start by looking at what's happening as though you were using var to iterate through your questions
Put simply, it's making it an immediately-invoked function expression (or IIFE for short) and passing in a parameter you normally wouldn't otherwise have access to.
When a click event handler callback expects a function with a single variable. When the event is handled, the function is invoked and the JS runtime provides a pointer event back to your function to do something with. That's all well and good, but here you want to know the offset of the clicked element in the array from information gleaned out of the scope of this callback.
So you instead change the callback shape. You pass in your own function and wrap it in parentheses. In JS, you pass in the arguments to the function in parentheses following the definition. Your example uses the lambda syntax, so you start with the function itself:
(e) => {return ...;}
But if this is all that were passed in here, you'd get a PointerEvent assigned to e as it matches the anticipated callback shape. So you instead need to wrap this in parentheses:
((e) => {return ...;})
Great, but you want this function to have a very particular value passed in when it executes, so you define the arguments at the end. You're using i as the variable identifying the index of the offset element, so we'll pass that in here.
((e) => { return ...; })(i)
Now, this means that when your event handler function is invoked for the first element, it'll practically look like the following:
((e) => {return ...; })(0); //Zero for the first zero-based index
This precludes the event handler callback assigning its own value to your first variable and means that now the function will be invoked, you'll pass the 0 (or otherwise set index property value) to your e argument and the remainder of the return statement will execute accordingly.
What's this closure thing I've heard of and how might it apply here?
#t.niese brings up a great point in the comments that I originally missed about closures and why they're quite relevant here.
Put simply, in JavaScript, you can refer to variables that are defined within a function's scope, to variables of the calling function's (e.g. parent) scope or any variables on the global scope. A closure is any function that's able to keep these references to these variables regardless of whether the parent has already returned or not.
If I have something like the following:
function listFruits(fruits) {
var suffix = "s";
for (var a = 0; a < fruits.length; a++) {
console.log(`I like ${fruits[a]}${suffix}`);
}
}
listFruits(['grape', 'apple', 'orange']);
// >> "I like grapes"
// >> "I like apples"
// >> "I like oranges"
As you'd expect, despite assigning suffix outside of the loop, I'm able to use it within the loop to make each fruit name plural. But I can do this using an inner function as well:
function listFruits(fruits) {
var suffix = "s";
function pluralizeName(name) {
return `${name}${suffix}`;
}
for (var a = 0; a < fruits.length; a++) {
var pluralName = pluralizeName(fruits[a]);
console.log(`I like ${pluralName}`);
}
}
listFruits(['grape', 'apple', 'orange']);
// >> "I like grapes"
// >> "I like apples"
// >> "I like oranges"
Again, despite suffix being assigned in the parent of the pluralizeName function, I'm still able to assign it in the return string to log to the console in my loop.
So, let's put a timeout callback in there and see what happens:
function listFruits(fruits) {
var suffix = "s";
for (var a = 0; a < fruits.length; a++) {
setTimeout(function() {
console.log(`I like ${fruits[a]}${suffix}`);
}, 1000);
}
}
listFruits(['grape', 'apple', 'orange']);
// >> "I like undefineds"
// >> "I like undefineds"
// >> "I like undefineds"
Why didn't we list the fruit we like here as before? a is still being defined in the parent and it does attach the suffix value as it should, so what gives?
Well, our loop starts at a = 0, sets the timeout to execute the callback in 1000ms, then increments a to 1, sets the timeout to execute the callback in 1000ms, then increments a to 2, sets the timeout to execute the callback in 1000ms, then increments a to 3, sets the timeout to execute the callback in 1000ms, then increments a to 4 at which point a is greater than the number of fruits passed in (3) and the loop breaks out. But when the timeout callbacks run then, a now has a value of 4 and fruits[4] is undefined, so we see that in the console logs.
Ok, but we want to be able to reference the value of our iterating index locally so the first callback works against the first object, the second against the second and so on, so how do we make that available to the callback? We use the IIFE approach covered above.
function listFruits(fruits) {
var suffix = "s";
for (var a = 0; a < fruits.length; a++) {
(function() {
var current = a;
setTimeout( function() {
console.log(`I like ${fruits[current]}${suffix}`);
}, 1000);
})();
}
}
// >> "I like grapes"
// >> "I like apples"
// >> "I like oranges"
It works here because we create a new closure for each of the functions created by the loop. When the function is created, we take the current value of a from the parent and assign it to a local variable so that when the setTimeout method fires later, it uses that local current value to properly access the intended index of the fruits array.
But rather than capture the variable as I do above in var current = a;, I can instead pass the a variable into the IIFE as a parameter and it will have exactly the same effect:
function listFruits(fruits) {
var suffix = "s";
for (var a = 0; a < fruits.length; a++) {
(function(current) {
setTimeout( function() {
console.log(`I like ${fruits[current]}${suffix}`);
}, 1000);
})(a);
}
}
listFruits(['grape', 'apple', 'orange']);
// >> "I like grapes"
// >> "I like apples"
// >> "I like oranges"
Our IIFE populates the current variable with the argument a passed in making it available locally and we get the expected outcome.
But my sample uses let, so how does that change anything?
Prior to ES6, we had only the global and function scopes requiring the use of the IFFEs to introduce local function scopes as we saw above. With ES6, we got a new "block scope" which essentially scopes everything within two curly braces, including any number of child blocks within that, but only if the variable is assigned using the const or let keywords. var still only assigns to a global or function scope.
Let's revisit the example above in which we received all the undefined values and replace our use of var with let.
function listFruits(fruits) {
var suffix = "s";
for (let a = 0; a < fruits.length; a++) { //Note the change to 'let' here
setTimeout(function() {
console.log(`I like ${fruits[a]}${suffix}`);
}, 1000);
}
}
listFruits(['grape', 'apple', 'orange']);
// >> "I like grapes"
// >> "I like apples"
// >> "I like oranges"
And this time it works because the value of a persists as assigned to the setTimeout callback's block scope.
Ok, so what about my sample?
Let's bring it back full-circle then. If you were using var, your sample scopes the present value of i to the IIFE so each of your event handler callbacks can access the appropriate offset node.
But since you're using let, the use of an IFFE certainly won't hurt anything, but it's unnecessary as the intended value of i is available to your callback functions via their block scope. As such, you can simplify to remove the IIFE and suffer no consequences.
for (let i = 0; i < questions.length; i++) {
questions[i].addEventListener(‘click’, function() {
if (clic[i].classList.contains(‘q-answered)) {
clic[i].classList.replace(‘q-answered’, ‘q-answeredno’);
} else if (clic[i].classList.contains(‘q-answeredno’)) {
clic[i].classList.replace(‘q-answeredno’, ‘q-answered’);
}
});
}
Should you have questions about any of this, please leave a comment and I'd be happy to edit to address it.

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
}

Calling correctly a closure with a return

This works fine:
var test = function(){
var a = 0;
console.log(a);
return function (){
a++;
return a;
};
};
var counter = test();
console.log(counter()); //1
console.log(counter()); //2
console.log(counter()); //3
console.log(counter()); //4
Why cannot I get same result by:
console.log(test());
and why do I need to work through a somewhat redundant proxy here (var counter), provided that the following works OK):
var test = function(k,l){
var c = k + l;
return c;
};
console.log(test(1,2));
Does this have something to do with the fact that there is a closure and I am calling a function which returns a function which returns some value in the end? Do I really need to define a new variable here or I can save time and lines of code and get it work directly?
Does this have something to do with the fact that there is a closure and I am calling a function which returns a function which returns some value in the end?
Yes it does. Your function test() does nothing else than setting up a variable, log 0 and then return another (operator) function which closes over the the scope of test().
Do I really need to define a new variable here or I can save time and lines of code and get it work directly?
Yes again. You need to hold a reference to that returned function by test(), which has an active scope-chain entry. Without any active reference (like assigning it to a variable) the garbage collector would clean it up.
It depends what you are trying to do.
You first example is a counter which holds state (remembers the last value). You can have multiple counters counting different things.
The second example is a function which just adds to numbers together but doesnt hold state or do anything different next time.
If you want to have a counter but without having to create it first you can do this:
var total = 0;
function count(){
total = total + 1;
return total;
}
So now:
console.log(count()); // 1
console.log(count()); // 2
console.log(count()); // 3
Or hide the variable in the function:
function counter(){
counter.total = (counter.total||0) + 1;
return counter.total;
}
Then:
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

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

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);
}

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.

Categories