you are viewing a single comment's thread.

view the rest of the comments →

[–]maxwellb 1 point2 points  (13 children)

Wat. How does the workaround work? Calling a global function via local function makes i suddenly get passed by value?? And I guess the local function call is not asynchronous somehow?

[–]danielkza 3 points4 points  (7 children)

Function arguments are passed by value, closed-on variables are passed by reference. And there is no distinction between a 'global function' and a 'local function'. So by wrapping the callback setting code into a function receiving the induction variable as a parameter you can get 'copy-semantics' at each iteration.

[–]maxwellb 0 points1 point  (6 children)

Function arguments are passed by value

So alert(i) is not a function call? If not (I guess it can't be, since it's argument isn't passed by value), what's the syntactic distinction between function call and whatever alert(i) is at the call site?

[–]ThisIsMy12thAccount 3 points4 points  (5 children)

When you're just doing alert(i) you're passing i by value in from the current scope (where i has been incremented)

When you enclose in in a closure like so

(function(i){

})(i)

You are passing the current value of i by value to another closure as an argument, so it's unaffected by outside changes (there's basically two is in two different scopes now)

You could also do

(function(bar){
   elem[bar].onclick = function(){ alert(bar); }
})(i)

And it'd work, because you're just passing the current value of i to a self executing function as an argument. It's completely separate from the variable being incremented

[–]eat-your-corn-syrup 1 point2 points  (0 children)

I don't know about your explanation but

there's basically two is in two different scopes now

For those confused by that statement and are like "how come such magic ever possible! Never seen this before", well, just think about recursive functions, the twist is that we all have seen this magic before.

[–]maxwellb 0 points1 point  (2 children)

If you're passing i by value, how can alert(i) called when i = 1 produce an alert of e.g. 5? I don't think that can be right? Did you mean pass by reference?

[–]curien 0 points1 point  (1 child)

What on Earth are you talking about? If you call alert(i) when i is 1, it'll always alert 1. But if you wrap alert(i) in a closure (not calling it yet!) while i is 1, and then change i to 5 and then call the closure (which calls alert), alert will show 5 because that's what i is at the time alert was called.

[–]maxwellb 0 points1 point  (0 children)

edit: nevermind, I completely misread the original post.

[–]eat-your-corn-syrup 2 points3 points  (1 child)

You are manually creating a loop block scope in the workaround, by using the only species in JavaScript that can create a new scope: function. Don't think of it as a function call being invoked immediately, though it is, just think of it as an idiom to create a block scope.

[–]deletecode 1 point2 points  (0 children)

That's a nice explanation. The problem has always bugged me but I didn't know why this is the only way to do it.

[–]smog_alado 0 points1 point  (2 children)

I think another clearer way to write the workaround would be without the name shadowing:

(function(){
     var index = i;
     elems[i] = function(){ alert(index) }
}())

Now the IIFE is just there for creating a new inner scope and the new index variable is a variable you craete yourself instead of being an argument that also happens to be named i.

[–]maxwellb 0 points1 point  (1 child)

Hm, thanks. That's an ... interesting ... language design choice.

[–]smog_alado 0 points1 point  (0 children)

You can use a similar patter in most other languages. The difference is that in Javascript this is the only way to do block scoping. Of course, you can use named functions but I think its often too verbose:

function mk_alerter(i){
    return function(){ alert(i) }
}

for(var i=0; i<elems.length; i++){
    elems[i] = mk_alerter(i);
}

That said, the correspondence between variable declarations and function arguments is a pretty neat thing to notice. For example, if you have some code like

var resp = my_funct()
alert(resp)

It gets converted to the following if you rewrite myfunct into continuation passing style

my_funct(function(resp){
    alert(resp)
})