This question already has answers here:
Increment value each time when you run function
(8 answers)
Closed last year.
I'm trying to store the value of i into a.
function sequence(i,a) {
i=a;
if (i==1) {
i++;
return strings[i]
}
else {
i=0;
i++;
a=i;
return strings[i]
}
};
strings is an array where I have stored five objects. When I call the function first time it enters else block, and then it enters else block second time as well. Is there a way to make the value of a=1 in the first attempt, and make it inter the if block.
I also tried declaring i=0 outside the function, and using just i++. But everytime the function is called, i becomes 0.
It would be great if we don't require 2 variables at all and are able to store and increment the value of i.
You can use a closure (basically a function that's returned from another one but that keeps track of the variables in its outer scope without making them global.)
// Simple array
const arr = [1, 2, 3, 4, 5];
// A button and a function that returns a new function
// (the closure) as the listener.
// Pass in the array of things to it
const button = document.querySelector('button');
button.addEventListener('click', handleClick(arr), false);
// The function accepts the array of things,
// and initialises the array count
function handleClick(arr, index = 0) {
// And we return a function that acts as the
// listener when the button is clicked. It increments
// the value until it hits the end of the array, and
// then starts again. The benefit of the closure is that
// it keeps a note of `index` so you don't have to
return function () {
console.log(arr[index]);
if (index === arr.length - 1) {
index = 0;
} else {
index++;
}
}
}
<button>Next</button>
Related
This question already has answers here:
Recursive function does not return specified value
(2 answers)
Closed 5 years ago.
I am a little baffled by this, but I have written a function that counts the number of dimensions in an array. This code executed just fine in my work environment, but now that I am in my personal environment, the function (countDims()) no longer returns a value. It appears to execute all the way up to the return statement. Here is the jsfiddle. Thoughts on why this might be?
And a snippet.
function constantArray(val,...dim){
// Function returns an nd-array of the given constant value. Note that the ellipsis in
// the function definition enables a variable number of arguments. Note that at least one
// dimension value must be given, and all desired dimension extents must be defined as
// integer lengths.
arr_out = [];
// The initial value forms the kernel of the array
for (i = 0; i < dim[dim.length - 1]; i++) {
arr_out.push(val);
}
// Reducing the dimension list on each pass provides a natural stopping point for recursion
dim.pop(dim[dim.length - 1]);
if (dim.length == 0) {
return arr_out;
}
else {
// Note that the ellipsis in the function call allows us to pass the remaining dimensions
// as a list. In this context, the ellipsis is the "spread" operator.
return constantArray(arr_out, ...dim);
}
}
function countDims(arr, dim_cnt){
// Function returns the number of dimensions in an array. Note that we keep the dimension
// count in the function arguments to ease updating during recursive calls.
if (dim_cnt == undefined) {dim_cnt = 0};
if (Array.isArray(arr)) {
dim_cnt++;
countDims(arr[0], dim_cnt);
}
else {
console.log("The dimension count of this array is "+dim_cnt);
console.log("I am in the return space!")
return dim_cnt;
}
}
x = constantArray(0, 4, 5)
console.log(x)
x_dims = countDims(x)
console.log(x_dims)
Did you forgot to return in the first condition of countDims?
if (Array.isArray(arr)) {
dim_cnt++;
return countDims(arr[0], dim_cnt);
function constantArray(val, ...dim) {
// Function returns an nd-array of the given constant value. Note that the ellipsis in
// the function definition enables a variable number of arguments. Note that at least one
// dimension value must be given, and all desired dimension extents must be defined as
// integer lengths.
var arr_out = [];
// The initial value forms the kernel of the array
for (let i = 0; i < dim[dim.length - 1]; i++) {
arr_out.push(val);
}
// Reducing the dimension list on each pass provides a natural stopping point for recursion
dim.pop(dim[dim.length - 1]);
if (dim.length == 0) {
return arr_out;
} else {
// Note that the ellipsis in the function call allows us to pass the remaining dimensions
// as a list. In this context, the ellipsis is the "spread" operator.
return constantArray(arr_out, ...dim);
}
}
function countDims(arr, dim_cnt = 0) {
// Function returns the number of dimensions in an array. Note that we keep the dimension
// count in the function arguments to ease updating during recursive calls.
//if (dim_cnt == undefined) {dim_cnt = 0};
if (Array.isArray(arr)) {
dim_cnt++;
return countDims(arr[0], dim_cnt);
} else {
console.log("The dimension count of this array is " + dim_cnt);
console.log("I am in the return space!")
debugger
return dim_cnt;
}
}
var x = constantArray(0, 4, 5)
console.log(x)
var x_dims = countDims(x)
console.log(x_dims)
you need to return result of countDims() call.
if (Array.isArray(arr)) {
dim_cnt++;
// Here
return countDims(arr[0], dim_cnt);
} else {
This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 5 years ago.
I am trying to understand how javascript function returns function handle and why following code prints '2' instead of '1'
var num = 01;
function processAndIncrement() {
var process = function() {
console.log(num);
}
num++;
return process;
}
var proCall = processAndIncrement();
proCall();
The num variable is independent of the functions in your code. Both functions refer to the same variable, so they both mutate the same data and observe the changes.
So the num++ operation increments num before proCall is ever invoked. When proCall is finally invoked, it sees the incremented state of num, because it has already been incremented.
Here's a step-by-step description of the order of operations (ignoring hoisting). Follow the sequence of numbers.
// 1. create the variable
var num = 01;
// 2. create the processAndIncrement function
function processAndIncrement() {
// 4. Assign a function that logs `num` to the `process` variable
var process = function() {
// 8. log the current value of `num` (which was incremented at step 5)
console.log(num);
}
// 5. increment `num`
num++;
// 6. return the `process` function
return process;
}
// 3. Invoke processAndIncrement, and assign its return value to `proCall`
var proCall = processAndIncrement();
// 7. invoke `proCall` (which is the same as the `process` function)
proCall();
If you wanted the increment to happen when proCall is invoked, then num++ should be moved to be inside the process function.
The Code prints 2 because you increment num which's value is already 1 (01 == 1).
This question already has answers here:
Explanation of `let` and block scoping with for loops
(5 answers)
Closed 5 years ago.
The let statement is advocated for usage in for loops as replacement for var declaration of the iteration variable.
By using let the user can forgo the usage of immediate invocation function
to preserve the value of iteration variable for callbacks for instance.
From mdn:
var list = document.getElementById('list');
for (let i = 1; i <= 5; i++) {
let item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
list.appendChild(item);
}
// to achieve the same effect with 'var'
// you have to create a different context
// using a closure to preserve the value
for (var i = 1; i <= 5; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));
(function(i){
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
})(i);
list.appendChild(item);
}
If I update the iteration variable i at the end of the loop, the for structure will pick it up and use the new value in the comperator logic of the loop and transfer that value to the next execution of the block.
console.log("start");
for (let i = 0; i < 5; i++) {
console.log("in loop", i);
setTimeout(function () {
console.log(i);
}, 0);
i = i + 2;
}
console.log("end");
Results:
start
in loop 0
in loop 3
end
undefined
2
5
The changes in the second iteration, do not propagate to the first iteration timeout callback.
Currently I thing the for loop implementation create a block execution context for each iteration, transfer the iteration variable as param, and specifically extract its value after the execution of the block ends and use it in the for mechanism for the next iteration. But it would be interesting to know if this is true, or not, and how it is implemented.
There is not much to say. Your observation is correct. There is a separate block for each iteration. So, no matter how long your timeout is, you will always see the same result.
At the end of each iteration the value of i will be passed to the next iteration. Since i is not an object, it will not be passed by reference. Thus a modification/incrementation of i in the next iteration won't affect the value of i in the block of the previous iteration.
Just for fun, try using an object instead of a primitive number:
console.log("start");
for (let i = {i:0}; i.i < 5; i.i++) {
console.log("in loop", i.i);
setTimeout(function () {
console.log(i.i);
}, 0);
i.i = i.i + 2;
}
console.log("end");
Results:
start
in loop 0
in loop 3
end
undefined
6
6
As you can see, in that case your i.i variable will actually be passed by reference and thus you can modify it over different iteration blocks.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
Say I have this function:
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
setTimeout( function() {
console.log( fruits[i]);
}, i * 1000);
}
}
printFruits(["Lemon", "Orange", "Mango"])
So this returns undefined 3 times.
I can see on a high level that since variables are stored not by value but by reference inside the closure... the loop is finishing first and by the time the functions are dequeued from maybe the Event Loop... the variable is already at undefined (fruits.length evaluates to 3 which is too high for this array size). But why does this perform strangely... it prints "apple" 3 times.
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
var someConstant = i;
setTimeout( function() {
console.log( fruits[someConstant]);
}, someConstant * 100);
}
}
printFruits(["mango", "banana", "apple"])
Shouldn't someConstant be changing as well with i? Why does it seem to be 2 always?
Also this works:
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
(function() {
var current = i;
setTimeout( function() {
console.log( fruits[current]);
}, current * 1000);
})();
}
}
Why is the IIFE necessary to fix this problem?
2nd Example
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
var someConstant = i;
setTimeout(function() {
console.log(fruits[someConstant]);
}, someConstant * 1000);
}
}
printFruits(["Lemon", "Orange", "Mango"])
This logs thrice Mango. Because everytime the someConstant variable is created and re-initialised to i. Recollect how for loop works. i-value is increasing here till 4, checks the condition 4<3, and terminates. So the matter inside the loop executes only thrice. So the last value of someConstant defined in the printFruits functional scope is 2. So when the inner function executes someConstant, its equal to 2. So we get each time Mango.
3rd example
function printFruits(fruits) {
for (var i = 0; i < fruits.length; i++) {
(function() {
var current = i;
setTimeout(function() {
console.log(fruits[current]);
}, current * 1000);
})();
}
}
printFruits(["Lemon", "Orange", "Mango"])
Here the beauty of closures happening. Here its a self executing function being executed, immediately. So when i = 1, it invokes immediately. Every function has a different scope now. There is a seperate current value defined for each. So later when it executes, it recollects whats the value of 'current' when it was defined inside its scope.
The only difference between these samples is that the for loop increments i to 3 before stopping, while the last value which is assigned to someConstant is 2. Your "working" code is outputting Mango three times (index 2 of the array) instead of undefined (index 3 of the array). The general behaviour is the same.
Yes, you do need an IIFE, or ES6's let keyword instead of var.
The difference is that someConstant never gets incremented after the last iteration. The for() loop sets i = 3, the test i < fruits.length fails, so the loop stops. As a result, someConstant is still set to 2 from the last iteration of the loop. Then all the callbacks run, so they all log fruits[2], which is Mango.
You need the IIFE to get each iteration to save its value of i in the closure.
Ok, I understand that in the code below a closure is created when the parent function counter returns an object with a method called count. But can somebody clarify if I'm understanding the control flow correctly? After the counter function is assigned to variable c the first time the method it is invoked, by c.count(), the output is 1. And each additional time it is called the stored n is incremented and the new value is returned. I understand this is possible through the magic of closure so that by calling this four times I get a new value each time ending with 4 instead of simply getting 1 four times over. But where is the value that n holds each time "hanging out" while it waits for me to call the count method again to increment the most recently created value? What will it take for n to have a clean slate again as 0? If I declare a new variable and assign the function counter to it? Trying to wrap my head around all this and these questions occurred to me. I'm not sure if what I'm asking is obnoxious or painfully obvious-- thank you in advance.
function counter() {
var n = 0;
return {
count: function() { return ++n; },
};
}
var c = counter();
console.log(c.count()); // 1
console.log(c.count()); // 2
console.log(c.count()); // 3
console.log(c.count()); // 4
where is the value that n holds each time "hanging out" while it waits for me to call the count method again to increment the most recently created value?
Variable n, of course, is hold in memory. Its behavior is just like on global scope, but within the counter scope.
What will it take for n to have a clean slate again as 0?
Reset it.
I've rewritten your example in self-executing function style to make it clearer. Hope it helps.
var c = (function counter(){
var n = 0;
return {
count: function() { return ++n; },
//don't use keyword 'var', it will create a new variable instead of reseting n
reset: function() { n = 0},
};
})();
console.log(c.count()); // 1
console.log(c.count()); // 2
console.log(c.count()); // 3
console.log(c.count()); // 4
console.log(c.reset());
console.log(c.count()); // 1
console.log(c.count()); // 2
console.log(c.count()); // 3
console.log(c.count()); // 4
Variables in a closure are not stored on any object available from within JavaScript itself, so it is impossible to reset it unless you add another function for it in the object you return:
function counter() {
var n = 0;
return {
count: function() { return ++n; },
reset: function() { n = 0; }
};
}