0

I have a simple function which has another function inside its for loop. The main function returns the sub function. When the main function is called, the loop runs but the sub function is not yet executed since it is not yet called. The sub function is called after the loop has been executed and hence the value of i points to the value of the last element of the array. If I want this to have a new binding to each of the values in the array, how do I fix it?

function getName() {
    const arr = ['a', 'b', 'c', 'd'];
    for (let i = 0; i < arr.length; i++) {
        function sendName() {
            alert(arr[i]);
        }
    }
    return sendName;
}
var receiveName = getName();
receiveName();
Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
Kshri
  • 414
  • 3
  • 16
  • 1
    What you're asking doesn't seem to make sense. You make a single call to `getName()` which returns a function to be called later. How can that single call have "a new binding to each of the values"? It's a single result from `getName()`. Maybe you could return an array of functions? – freedomn-m Dec 30 '17 at 14:16
  • Your code wouldn't even work in strict mode, as [functions are block-scoped](https://stackoverflow.com/a/31461615/1048572). The `sendName` variable you return does not exist. Which is part of your larger problem: Your `getName()` returns only a single function. How would you even call different functions which point to different indices (which is the part that actually already works due to your usage of `let` in the loop). – Bergi Dec 30 '17 at 14:24
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Dexygen Dec 30 '17 at 14:28

5 Answers5

2

You can use the bind function to achieve what you want:

function sendName( name ) {
  alert( name );
}

function getNames() {
  const arr = [ 'a','b','c','d' ];
  let sendNames = []; //Dunno what you want to do with this functions so let's store them in an array
  for(let i = 0; i < arr.length; i++) {
    sendNames.push(sendName.bind(this, arr[i])); // this - context, later arguments
  }
 return sendNames;
}
var receivedNames = getNames();
receivedNames[1](); //b
receivedNames[3](); //d
Oskar
  • 2,548
  • 1
  • 20
  • 22
  • When you add a negative vote to my answer, please add a feedback in comment so I could understand what's wrong. – Oskar Dec 30 '17 at 14:24
  • Why so complicated? The code where the OP creates the functions already works, only the bit with the array doesn't. – Bergi Dec 30 '17 at 14:25
  • 1
    @Bergi In the other case, with the self-invoking function, you can just call `alert(arr[i])`, I thought that the essence of the question is how to delay the invocation to later time – Oskar Dec 30 '17 at 14:28
  • What is `this` that you are even binding? – charlietfl Dec 30 '17 at 15:03
  • @charlietfl It's current's function scope/context. In this example it doesn't really matter as we're not using it directly in our `sendName` function, we just care about currying the parameter – Oskar Dec 30 '17 at 15:15
  • @Oskar Not "in the other case". The `alert(arr[i])` would work in this case as well. The IIFE is not necessary with block scoping. – Bergi Dec 30 '17 at 18:37
  • @Oskar I think it's better to pass `null` for the `this` argument then. – Bergi Dec 30 '17 at 18:38
1

You might want to use an anonymous wrapper instead:

function getName() {
    const arr = ['a', 'b', 'c', 'd'];
    for (let i = 0; i < arr.length; i++) {
        (function() {
            alert(arr[i]);
        })(i);
    }
}
getName();

Also, as other people mentioned in comments, such implementation makes little sense until you provide more detailed explanation about why do you need it. There probably should be more elegant solutions that will fit your requirements.

Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
1

You can use array to store different functions. This example will return different letters according to N.

function getName() {
const arr=['a','b','c','d'];
  let a = [];
 for(let i = 0;i<arr.length;i++) {
  a.push(function () {
   alert(arr[i]);
  });
 }
 return a;
}
var receiveName=getName();
let N = 0; // returns a
receiveName[N]();
console.log(receiveName);

Hope it helps

Shalitha Suranga
  • 1,138
  • 8
  • 24
1

I think there is no point to wrap alert(a[i]) in function scope as we already are using let and it provides a block scope.

We could just use the following function and it can output the required result.

function getName() {
    const arr = ['a', 'b', 'c', 'd'];
    for (let i = 0; i < arr.length; i++) {
            alert(arr[i]);
    }
}
getName();
nirazlatu
  • 983
  • 8
  • 18
0

This is because its getting called only once ie... even though

function sendName(){
 alert(arr[i]);
    }

sendName is changing its assigned value four times but it is called only once, therefore it is only alerting "d", since d is the last value assigned. If you want to alert "a", "b", "c", "d", call the function inside the loop.

Shruti Singh
  • 181
  • 1
  • 11