0

Can someone please explain how this gets 50, and how does the algorithm works. Thanks.

var multipliers = function makeMultipliers (max) { 
    var result = [];
    for (var i = 0; i < max; i++)
        result.push (function (x) {return x * i;}); 
    return result;
}

multipliers(10) [2] (5)
???

what’s the value?

50, not 5

can you fix it?

Source: http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-170-software-studio-spring-2013/lecture-notes/MIT6_170S13_35-java-fn-cls.pdf

page 30

jfriend00
  • 683,504
  • 96
  • 985
  • 979
Taiwei Tuan
  • 430
  • 2
  • 12
  • see: http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops/3572616#3572616 – slebetman Feb 10 '15 at 06:44

2 Answers2

0

This code creates an array of functions which will incorrectly all end up referring to the final value of i, but which are intended to freeze the value of i during each iteration.

To fix it you simply could add a level of locality to the iteration variable by embedding a new local variable that freezes the value of i within another function. For example, below the existing local variable i is passed into the dummy function as an argument, and the value of i at that instant is frozen into the new local variable i which is independent of the original i and not changed by the subsequent iterations of the original i.

result.push (function (x) {return x * i;})

result.push((function(i) {
    return function (x) {return x * i;};
})(i));
Joseph Myers
  • 6,434
  • 27
  • 36
0

It's a scope issue. By the time the last function is called, i is where it was last seen at that scope last which would be 10. You can prevent this with a closure.

function multipliers(max){
  var a = [];
  for(var i=0; i<max; i++){
    (function(i){
      a.push(function(x){return x*i});
    })(i);
  }
  return a;
}

In this case, the closure is a self-executing function that scopes off i at each step of the loop. If this did not happen the scope would go to the multipliers function itself. Since the loop has already completed by the time you are calling the function which you pass 5 to, which happens to be at step 2 of the Array that multipliers returns, i has already reached 10. Obviously, 10*5 is 50. When the closure is used is last position is 2 within the scope it is in, giving you the result that is sought. 5*2 = 10.

console.log(multipliers(10)[2](5));
StackSlave
  • 10,613
  • 2
  • 18
  • 35
  • Technically it's a closure issue. Because of closures the variable `i` is shared across all the functions in `a` (that's what closures do - they share stack frames). You can prevent this by ***breaking the closure***. – slebetman Feb 10 '15 at 06:43
  • Call it whatever you want. In this case the scope of `i` creates the undesired results. – StackSlave Feb 10 '15 at 06:45
  • Yeah. But you don't prevent it with a closure - the closure is the SOURCE of your scope problem. You prevent it by breaking the closure. You break a closure by passing via function argument. – slebetman Feb 10 '15 at 06:48
  • You are really scoping off `i`, because you are passing that argument into the self-executing function where it is closed off. Multiple books I have read taught me this. I have never heard this breaking the closure term, myself. – StackSlave Feb 10 '15 at 07:00
  • Yes, that's exactly what it's doing. However, this mechanism is not called a "closure". The closure is the mechanism that causes `i` to be shared in the first place. Your scoping off the `i` is intended remove it from the closure. – slebetman Feb 10 '15 at 07:02
  • This answer is technically about something else but to answer the question I had to explain what a closure is. See this for an explanation of what closures are: http://stackoverflow.com/a/26063201/167735 – slebetman Feb 10 '15 at 07:04
  • In this case the Anonymous self-executing function refers to the independent var `i` in the for loop. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures – StackSlave Feb 10 '15 at 07:05
  • That mozilla doc is impercise about breaking the closure. Technically it's correct that it's using a function factory to break the closure. Technically it's even correct to say use more closure because every function in javascript is a closure. But it should have been more specific in saying that what it means is to create a function where its closure only encloses the global scope. But it's wrong to say that that's the reason the solution works. – slebetman Feb 10 '15 at 07:13
  • Cool. An explanation from an unanswered Stack Overflow question. Still waiting for that "breaking the closure" link. What if code was not in a function? Where would this closure that you would be breaking be? Would it be at the global scope, even though there would be no closure? – StackSlave Feb 10 '15 at 07:16
  • What link are you waiting for? That the mechanism for why `i` is shared accross functions called a "closure"? It's in the mozilla doc you linked to. – slebetman Feb 10 '15 at 07:22
  • Code not inside function: `for(var i=0,l=someArray.length; i – StackSlave Feb 10 '15 at 07:23