2
\$\begingroup\$

I've recently try to brush up my javascript skills, so I have a friend who gives me puzzles from time to time to solve. Yesterday I got this :

function testFun() {
  f = {};
  for( var i=0 ; i<3 : i++ ) {
    f[i] = function() { alert("sum='+i+f.length); }
  }
return f;
}

Expected Results:
testFun()[0]() should alert “sum=0”
testFun()[1]() should alert “sum=2”
testFun()[2]() should alert “sum=4”

I did this which does like requested above:

function testFun() {
    var i,
        f = {};
    for (i = 0; i < 3; i++) {
        f[i] = (function(number) {
            return function() {
                alert("sum=" + (number * 2));
            }
        }(i));
    }
    return f;
}

Could my answer be written better and what can be improved if so?

\$\endgroup\$
  • 1
    \$\begingroup\$ I did not down-vote you but I think that your question was down-voted becase: 1) you ask several question in one post 2) you ask to help you with your own homework, which is not welcomed 3) you are asking for solution for second task without demonstrating you own effort 4) your second question better matches stackoverflow.com, not codereviews. Consider it "code reviewing" your post so you get what this site is for :) I would recommend to delete this one and repost trying to follow the rules. Good luck. \$\endgroup\$ – Alex Netkachov Nov 8 '13 at 17:49
  • 1
    \$\begingroup\$ @Malachi, thank you for the link, will keep it in mind. \$\endgroup\$ – Alex Netkachov Nov 8 '13 at 17:57
  • 1
    \$\begingroup\$ @AlexAtNet, BTW, it is on the top of the page along with the ABOUT page \$\endgroup\$ – Malachi Nov 8 '13 at 17:58
  • 1
    \$\begingroup\$ ok I edited my question to fit the rules, thanks for your comments \$\endgroup\$ – Gandalf StormCrow Nov 8 '13 at 17:58
  • 1
    \$\begingroup\$ it's all good, it is hard to see <voicetone> tags on the internet. so I read everything in a pleasant and helpful tone so that if I do take it the wrong way I still don't take offense. \$\endgroup\$ – Malachi Nov 8 '13 at 18:05
6
\$\begingroup\$

The style of you code is good. Here is not much of it but it looks like you understand the concept of closures well.

The logic of the original task is changed so even if you code returns the right values, they are calculated differently. The original task calculates it as i + f.length. It is questionable how to calculate length of an object. There are some ways but we can simply guess that the initialization of f is incorrect and f should be an array.

Another question is how to capture the current state of the array in the closure. Because it is object the closure will get the reference to it so it should be copied. The simplest way of copying an array it to call f.slice().

The last issue (reported by jshint) is that the function created in the loop is always the same. So you can move it outside of the loop and just call in the loop.

Update: Some notes on capturing objects in closures.

The closure captures variable by reference. If you have a loop with an index variable and capture this variable and then invoke the closure you will have not the value of the variable on the corresponding loop iteration. What you will get is the final value on the last loop iteration.

To capture the value the variable should be copied to some temporary context. In the languages with statement scope it is enough to declare local variable, but in JS with function scope you need to capture the variable by running the function. That is why in your code you have this function(number) { - it copies the index variable i to the function parameter number and when value of i is changed, value of number remains.

But when you capturing objects the situation is different, because objects are passed into function scope by reference too. So if the state of the object is changed, it will be changed in both parent and function scope. So in your example later, while invoking, the function will add the final length of the array to the variable, not the length of the array from the corresponding loop iteration.

So the length somehow should be preserved in the closure. It can be passed as number but it will be too boring - you already know how to deal with them. So it is better to think how to pass the copy of the array itself.

    f[i] = (function(number, array) {
        return function() {
            alert("sum=" + number + array.length);
        }
    }(i, f.slice()));
\$\endgroup\$
  • \$\begingroup\$ Thanks for the comments that was really useful, I changed f to be an array and put the function outside the for loop. Could you further explain the capturing array in the closure? what you mean by that. thanks \$\endgroup\$ – Gandalf StormCrow Nov 8 '13 at 18:45
  • \$\begingroup\$ I've added the nodes on objects. \$\endgroup\$ – Alex Netkachov Nov 8 '13 at 19:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.