Counting function - javascript

I need to have multiple counters so I created a function, the problem is that I can't store the value in the variable outside the function. I thought this would be easy but I couldn't find anything online.
myCounter = 2;
function count(counter) {
counter++;;
}
count(myCounter);
I would get the value in myCounter to update every time I call the function, thanks for any help you can provide

You can use a closure function that returns an object with a getter so you can initialize as many individual instances as you need
function counter() {
let _count = 0;
return {
get count() {
return ++_count;
}
}
}
let ct1 = new counter();
console.log('ct1', ct1.count, ct1.count)
let ct2 = new counter();
console.log('ct2', ct2.count, ct2.count, ct2.count)

Try this.
var myCounter = 2;
var myCounter2 = 3;
function count(counter) {
return ++counter;
}
myCounter = count(myCounter);
myCounter2 = count(myCounter2);
console.log('myCounter: ' + myCounter);
console.log('myCounter2: ' + myCounter2);

Related

Scope in generated Callback function

I have a generater function generation callback functions for eventlisteners:
function createCallback(counter){
return function(){
counter++;
}
}
The callback should just count how ofter a button was clicked. Lets generate two functions:
var counter1 = 0;
var counter2 = 0;
var callback1 = createCallback(counter1);
var callback2 = createCallback(counter2);
And register them as listeners:
button1.addEventListener("click", callback1);
button2.addEventListener("click", callback2);
Now I need a reset function:
function reset(){
counter1 = 0;
counter2 = 0;
}
I would have expected, that the generator function passes the reference to the global counters into the generated callback functions and the callback functions would modifiy the global variables. But they don't, as I learned from the reset function.
There is an issue with scope, but I do not get it.
Why and how do the generated callback functions have an own scope for their counters? EDIT Answer: Because the argument counter passed into the createCallback function is not passed as refernce into the generated function.
How could I bind/access the global counter1 and counter2?
EDIT
Since I already learned, that the 'counter' variable is not passed as reference: How can I generate a function with a reference to a global variable?
You can use an object to store the counters and pass property names around:
const counters = {
a: 0,
b: 0
};
function createCallback(counterName){
return function(){
counters[counterName]++;
}
}
button1.addEventListener("click", createCallback("a"));
button2.addEventListener("click", createCallback("b"));
function reset(){
counters.a = 0;
counters.b = 0;
}
(If you don't need individual names, use an array with indices instead)
The alternative is to create multiple closures over the same variable for the different functionalities, and keep them in one object per counter. That's the OOP way:
function createCounter() {
var counter = 0;
return {
getCount() {
return counter;
},
reset() {
counter = 0;
},
increment() {
counter++;
}
};
}
const counter1 = createCounter();
const counter2 = createCounter();
button1.addEventListener("click", counter1.increment);
button2.addEventListener("click", counter2.increment);
function reset(){
counter1.reset();
counter2.reset();
}
(again, use an array of counters instead of multiple individually named variables if you need arbitrarily many)
Objects in JS are pased by reference (by reference value actually), so this will work (setTimeouts are used here to simulate the callbacks)
function createCallback(counter){
return function(){
counter.count++;
}
}
function reset(){
counter1.count = 0;
counter2.count = 0;
}
var counter1 = {count: 0};
var counter2 = {count: 0};
var callback1 = createCallback(counter1);
var callback2 = createCallback(counter2);
setTimeout(function(){
callback1();
document.getElementById('result').innerHTML += counter1.count + ' ';
}, 1000);
setTimeout(function(){
callback1();
document.getElementById('result').innerHTML += counter1.count + ' ';
}, 2000);
setTimeout(function(){
callback1();
document.getElementById('result').innerHTML += counter1.count + ' ';
}, 3000);
setTimeout(function(){
reset();
document.getElementById('result').innerHTML += counter1.count + ' ';
}, 4000);
<div id="result"></div>
In Javascript, primitive type variables like strings and numbers are always passed by value.
So you could use object instead of primitive vars:
Embed your global var inside an object:
var myCounterObj = {
counter1: 0,
counter2: 0
}
Change your callback definition in this way:
function createCallback(counter, name) {
return function() {
counter[name]++;
console.log("My counter is: " + counter[name]);
}
}
Then create the callback vars:
var callback1 = createCallback(myCounterObj, 'counter1');
var callback2 = createCallback(myCounterObj, 'counter2');
And finally change the reset function:
function reset() {
myCounterObj.counter1 = 0;
myCounterObj.counter2 = 0;
}
Hope this helps!
The variable counter (counter1, counter2) is passed by value, bot by reference. To make it work, the variables should be changed into objects:
var counter1 = {"value": 0};
var counter2 = {"value": 0};
Those can be passed into the generator function. The increment has to be changed accordingly:
function generate(counter){
return function(){
counter.value++;
}
}
As well as the reset function:
function reset(){
counter1.value = 0;
counter2.value = 0;
}
Now it works.

How to make the return variable hold a new value after Increment/Decrementing it in a function?

Everytime I run this function, the p1_Balance will always reset back to 10 and will not hold the new value of an increment or decrement.
function Balance() {
var p1_Balance=10;
var x= Math.floor(10*Math.random());
if (x<5) {
p1_Balance=p1_Balance-1;
} else {
p1_Balance=p1_Balance+1;
}
return p1_Balance;
}
Pass p1_Balance into the function instead of initializing it each time the function is called with: var p1_Balance = 10;
p1_Balance should be declared outside the scope of the function (meaning not within the function itself). Otherwise, each time the function is called, the initializer that sets the value to 10 runs as well.
var p1_Balance=10;
function Balance(){ ...
You can use Javascript closures to create a function that does what you want, as you can see below:
var Balance = (function() {
var p1_Balance = 10;
return function() {
var x = Math.floor(10 * Math.random());
if (x < 5)
return p1_Balance += 1;
else
return p1_Balance -= 1;
};
})();
for (var i = 0; i < 10; i++)
console.log(Balance());
Alternatively, you will need to define the p1_Balance variable outside the function or pass it as an argument.
There could be several solutions:
one is declaring p1_Balance as a global variable.
var p1_Balance=10;
function Balance(){
var x= Math.floor(10*Math.random());
if (x<5) {
p1_Balance=p1_Balance-1;
}
else {
p1_Balance=p1_Balance+1;
}
return p1_Balance;
}
another is you could pass balance as a function parameter:
function Balance(p1_Balance){
var x= Math.floor(10*Math.random());
if (x<5) {
p1_Balance=p1_Balance-1;
}
else {
p1_Balance=p1_Balance+1;
}
return p1_Balance;
}
.....
value = Balance(10);// value=something that you want to change by that function.

Passing Javascript Parameters and Assigning to Variables

i want to pass one function generated value to another function.
example :
function mainFunc(){
var x = 1;
}
function doMatch(){
var tot = x + 1;
}
only if doMath() Function calls it will calculate total. it shouldn't call mainFunc() Again.
not like this
function mainFunc(){
var x = 1;
return x;
}
function doMatch(){
var tot = mainFunc() + 1;
}
How can i do this. i think if we can make it global variable after it generated from the mainFunc() then every other function can access it.
we cant edit global variable value from function right?
You can define a variable outside the scope of both of these methods that is accessible from each.
function mainFunc(){
window.x = 1;
}
function doMatch(){
var tot = x + 1;
}

Return a number from a constructor

I've been my banging head against the wall with the above question. Let's say I have the following class:
function Counter() {...}
so when I call the constructor:
var c= new Counter();
console.log(c); //return 0
furthermore If I created the following method:
Counter.prototype.increment = function() {
return this += 1;
};
it should increment c by 1 for every call
c.increment(); // return c=1
c.increment(); // return c=2
so far I have come up with:
function Counter(){return Number(0)}
but still returns Number{} not a zero...
Any thoughts?
Thanks in advance!
JavaScript doesn't allow for a custom Object type to directly imitate a primitive value. It also doesn't allow this to be assigned a new value.
You'll have to instead store the value within a property:
function Counter() {
this.value = 0;
}
var c = new Counter();
console.log(c); // Counter { value: 0 }
And, increment the value from it:
Counter.prototype.increment = function () {
this.value += 1;
};
c.increment();
console.log(c.value); // 1
Though, you can at least specify how the object should be converted to a primitive with a custom valueOf() method:
Counter.prototype.valueOf = function () {
return this.value;
};
console.log(c.value); // 1
console.log(c + 2); // 3
You can't return a value from the constructor because you instantiate it using the new keyword, this gives you a new instance of the object.
Store a property and increment that instead:
function Counter() {
this.count = 0;
}
Counter.prototype.increment = function() {
this.count++;
return this.count;
};
var c= new Counter();
console.log( c.increment() ); // 1
console.log( c.increment() ); // 2
console.log( c.increment() ); // 3
This is your problem:
Counter.prototype.increment = function() {
return this += 1;
};
this is an object, += is not defined for objects.

Javascript: Using changing global variables in setTimeout

I am working on Javascript and using firefox scratchpad for executing it. I have a global index which I want to fetch inside my setTimeout (or any function executed asynchronously). I can't use Array.push as the order of data must remain as if it is executed sequentially. Here is my code:-
function Demo() {
this.arr = [];
this.counter = 0;
this.setMember = function() {
var self = this;
for(; this.counter < 10; this.counter++){
var index = this.counter;
setTimeout(function(){
self.arr[index] = 'I am John!';
}, 100);
}
};
this.logMember = function() {
console.log(this.arr);
};
}
var d = new Demo();
d.setMember();
setTimeout(function(){
d.logMember();
}, 1000);
Here, I wanted my d.arr to have 0 - 9 indexes, all having 'I am John!', but only 9th index is having 'I am John!'. I thought, saving this.counter into index local variable will take a snapshot of this.counter. Can anybody please help me understand whats wrong with my code?
The problem in this case has to do with scoping in JS.
Since there is no block scope, it's basically equivalent to
this.setMember = function() {
var self = this;
var index;
for(; this.counter < 10; this.counter++){
index = this.counter;
setTimeout(function(){
self.arr[index] = 'I am John!';
}, 100);
}
};
Of course, since the assignment is asynchronous, the loop will run to completion, setting index to 9. Then the function will execute 10 times after 100ms.
There are several ways you can do this:
IIFE (Immediately invoked function expression) + closure
this.setMember = function() {
var self = this;
var index;
for(; this.counter < 10; this.counter++){
index = this.counter;
setTimeout((function (i) {
return function(){
self.arr[i] = 'I am John!';
}
})(index), 100);
}
};
Here we create an anonymous function, immediately call it with the index, which then returns a function which will do the assignment. The current value of index is saved as i in the closure scope and the assignment is correct
Similar to 1 but using a separate method
this.createAssignmentCallback = function (index) {
var self = this;
return function () {
self.arr[index] = 'I am John!';
};
};
this.setMember = function() {
var self = this;
var index;
for(; this.counter < 10; this.counter++){
index = this.counter;
setTimeout(this.createAssignmentCallback(index), 100);
}
};
Using Function.prototype.bind
this.setMember = function() {
for(; this.counter < 10; this.counter++){
setTimeout(function(i){
this.arr[i] = 'I am John!';
}.bind(this, this.counter), 100);
}
};
Since all we care about is getting the right kind of i into the function, we can make use of the second argument of bind, which partially applies a function to make sure it will be called with the current index later. We can also get rid of the self = this line since we can directly bind the this value of the function called. We can of course also get rid of the index variable and use this.counter directly, making it even more concise.
Personally I think the third solution is the best.
It's short, elegant, and does exactly what we need.
Everything else is more a hack to accomplish things the language did not support at the time.
Since we have bind, there is no better way to solve this.
The setTimeout doesn't have a snapshot of index as you are expecting. All of the timeouts will see the index as the final iteration because your loop completes before the timeouts fire. You can wrap it in a closure and pass index in, which means the index in the closure is protected from any changes to the global index.
(function(index){
setTimeout(function(){
self.arr[index] = 'I am John!';
}, 100);
})(index);
The reason is that by the time settimeout is started, the for loop is finished executing the index value is 9 so all the timers are basically setting the arr[9].
The previous answer is correct but the source code provided is wrong, there is a mistyping elf in place of self . The solutions works.
An other way , without a closure , is to just add the index parameter to the function declaration in the setTimeout statement
function Demo() {
this.arr = new Array();
this.counter = 0;
this.setMember = function() {
var self = this;
for(; this.counter < 10; this.counter++){
var index = this.counter;
setTimeout(function(){
self.arr[index] = 'I am John!';
}(index), 100);
}
};
this.logMember = function() {
console.log(this.arr);
};
}
var d = new Demo();
d.setMember();
setTimeout(function(){
d.logMember();
}, 1000);

Categories