Closures in Javascript: assigning local variable doesn't work - javascript

Just when I thought I understood closures...
The following code snippet:
function f() {
var a = [];
var i;
for (i = 0; i < 3; i++) {
a[i] = function () {
var x = i;
return x;
}
}
return a;
}
var a = f();
console.log(a[0]());
console.log(a[1]());
console.log(a[2]());
prints out 3, 3, 3. I don't understand why. I'm copying the value of 'i' to the local variable x, so there should be three x's: x0=0, x1=1. x2=2. How are all of them reading the final value of i?

Your problem is caused by each a[i] being, in fact, a closure. They all share the same i, which is evaluated when each a[i] is called, not when the loop executes. You need to create each closure with a separate context. For instance:
function f() {
var a = [];
var i;
for (i = 0; i < 3; i++) {
a[i] = makeClosure(i);
}
return a;
}
function makeClosure(i) {
return function () {
var x = i;
return x;
}
}

Even though the value of i changes in your for loop, it's still the same i variable. You need to shadow i in that scope and effectively pass it by value:
for (var i = 0; i < 3; i++) {
(function(x) {
a[x] = function() {
return x;
}
})(i);
}

Related

Javascript pass counter to function

I know how do closures work, but it is not very clear to me.
How does the below snippet works under the hood (output's 0) :
function fillFunctionArr() {
let arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = {
inner: i,
innerFunc: function() {
console.log(this.inner)
}
}
}
return arr;
}
var ex = fillFunctionArr();
ex[0].innerFunc()
Sure, if it were declared as :
arr[i] = {
// inner: i,
innerFunc: function() {
console.log(i)
}
}
The output will be 10.
Why is the first snippet more preferable than the below one:
function fillFunctionArr() {
let arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = (function(qnt) {
return function() {
console.log(qnt)
}
})(i);
}
return arr;
}
var ex = fillFunctionArr();
ex[0]()
There is no 'preferable' choice. The choice depends on logic of outer code, that you want to extend. Both initialized immediately on cycle iteration. It's just one of the edges of JS flexibility.

Javascript: How to implement iterator over multidimensional array?

I have 2 dim array, which looks like this:
var a = [[1,2,3],[4,5,6],[7,8,9]];
I want to write an iterator which will return one value a time when it's called.
iterator(); //returns 1
iterator(); //returns 2
iterator(); //returns 3
I tried such approach:
function iterator() {
var a = [[1,2,3],[4,5,6],[7,8,9]];
var i, j;
return function() {
for (var i = 0; i < a.length; i++) {
var b = a[i];
for (var j = 0; j < b.length; j++) {
return a[i][j];
}
}
}
};
var a = iterator();
a(); //1
a(); //1
a(); //1
It always returns me first element.
I can try this one:
function iterator() {
var a = [[1,2,3],[4,5,6],[7,8,9]];
var i = 0, j = 0;
return function() {
for (; i < a.length; i++) {
var b = a[i];
for (; j < b.length; j++) {
return a[i][j];
}
}
}
};
Also not works.
But if I try this one:
function test() {
var a = [1,2,3,4,5], i = 0;
return function() {
while (i < a.length) {
return a[i++];
}
}
}
var a = test();
a(); //1
a(); //2
a(); //3
It works fine.
What is the difference here? How to make for loop work?
One other visible problem for me is bounds. How should I stop when I reach array bounds?
Instead of using an inner for-loop for the second dimension you can use a simple if to test the bounds of j
function iterator() {
var a = [[1,2,3],[4,5,6],[7,8,9]];
var i = 0, j = 0;
return function() {
for (; i < a.length; i++) {
var b = a[i];
if (j < b.length){
return a[i][j++];
}
j = 0;
}
return undefined; // reached when there is no value left
}
};
Why do you need a loop at all in this case. You are effectively flattening the array anyway. You could just increment the indices along with a bounds check:
function iterator() {
var a = [[1,2,3],[4,5,6],[7,8,9]], i = 0, j = 0;
return function() {
if (j >= a[i].length) { j = 0; i++; }
if (i >= a.length) { j = 0; i = 0; }
snippet.log( a[i][j++] );
}
};
var x = iterator();
x(); x(); x(); x(); x(); x(); x(); x(); x(); x();
x(); x(); x(); x(); x(); x(); x(); x(); x(); x();
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
It is generally not a good idea to extend standard feature, but for a didactical example I will make an exception. For real use, I suggest to implement your own class.
General Idea
Array.prototype.beginIterator = function()
{
var counter = 0;
return function()
{
if (counter<=this.length) return this[counter++];
else return undefined;
};
}
Then you could iterate like follow:
var a = [3,1,4,1,5];
var it = a.beginIterator();
for (var i=it(); i!=undefined; i=it())
{
alert(i);
}
This at the moment work only with single-dimension array, but it could be applied with any logic to other arrays or object.
Multi-dimensional (Any) solution:
The following iterator allow any-dimension array in any combination:
Array.prototype.beginIterator = function()
{
var counter = 0;
var iterators = null;
return function()
{
val = undefined;
if (iterators!=null)
{
val = iterators();
if (val!==undefined) return val;
else
{
iterators = null;
counter++;
}
}
while (counter <=this.length)
{
if (!(this[counter] instanceof Array)) return this[counter++];
else
{
iterators = this[counter++].beginIterator();
val = iterators();
if (val!==undefined) return val;
}
}
return undefiend;
};
}
Example of use:
var a = [3,[3,5,7],4,[1,[2,5,8]],5];
var it = a.beginIterator();
for (var i=it(); i!=undefined; i=it())
{
alert(i);
}

Stop passing by reference in closures [duplicate]

This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I have code that looks like this:
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
}
}
Unfortunately, it seems that i is being passed by reference, so all the functions in a output 10. How do I make it so that each function outputs the value that i had when it was created? I.e. a[0]() gives 0, a[1]() gives 1, etc.
EDIT: to clarify, I do not want a to store the values 0-9. I want a to store functions that return the values 0-9.
You need to invoke a function (to create a closure that captures your value) which returns a function (the one you want to end up with). Something like this:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(value) {
return function() {
console.log(value);
}
})(i);
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = (function(j) {
return function () {
console.log(j);
}
})(i);
}
A better performing version -
function makeFunction(i) {
return function () {
console.log(i);
}
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = makeFunction(i);
}
JSFiddle Demo.

Javascript - Variable updating

This is probably an easy question but it's late at night and I can't get my head round this.
here's my code
$(document).ready(function () {
var items = getNumber();
for (var i = 0; i < items.length; i++) {
var test = items[i].action;
test();
}
});
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({ id: i, number: num, action: function () { alert(i) } });
}
return items
}
Could someone explain to me why the alert output is always 5? I want the alert parameter to be the index at the time it is added to the array. It seems like it is being dynamic.
If you could also post a solution how i could get this to work i would be extremely thankful
This is a common issue with JavaScript variable scoping: new variables are only introduced in a new execution context and thus, in the problematic code, i is shared across all the action callbacks.
Anyway, here is the corrected code following the standard idiom:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
// _i is a new variable for each callback
action: (function (_i) {
// use separate (one per callback) _i variable
return function () { alert(_i) }
})(i) // pass in current value for loop
});
}
return items
}
Alternatively, if one doesn't like all the nesting, it's fine to use a "named" function to perform the same task. The key to point is that the closure is created (and returned from) a new execution context so that a different variable is closed-over:
function getNumber()
{
function mkAction (i) {
return function () { alert(i) }
}
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: mkAction(i)
});
}
return items
}
Another approach is to use Function.bind from ES5:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: (function (_i) { alert(_i) }).bind(null, i)
});
}
return items
}
(Note that Function.bind can be emulated using a new execution context/closure even without native browser support. See the MDC documentation.)
See also:
JavaScript closure inside loops – simple practical example
Passing functions to setTimeout in a loop: always the last value?
How do JavaScript closures work?

Javascript giving parameters to inner function

So my basic setup is this:
for (var i = 0; i < 3; i++) {
var indices = [-1, -1, -1];
while (index == -1) {
// Do Stuff
index[i] = newIndex;
}
var press = function() { alert(i); };
new control({press: press});
}
Now when I press the each of the new controls instead of getting alert(0), alert(1) and alert(2) I get alert(3), alert(3) and alert(3).
I can kind of understand whats going on. Now my question: how can i pass the different indexes to the function as I intended?
It is because closure variable i, the solution is to create a private closure for each loop.
for (var i = 0; i < 3; i++) {
var indices = [-1, -1, -1];
while (index == -1) {
// Do Stuff
index[i] = newIndex;
}
var press = (function(myvar){
return function() { alert(myvar); };
})(i);
new control({press: press});
}
Use closure:
var press = (function (x) {
return function () {
alert(x);
};
})(i);
This way the current i value is saved in a safe place, a private function.
Note that declaring variables (with var) inside loops are not standard, you should declare the press variable outside the loop.

Categories

Resources