Hacker News new | past | comments | ask | show | jobs | submit login

Except that Javascript has lexical scope. Use the var keyword to define local variables.

    foo = function(){
        var bar=0
        return function(){
            bar=bar+1
            return bar
        }
    }
    baz=foo()

    baz() // => 1
    baz() // => 2
    baz() // => 3
    bar;  // => ERROR: bar is not defined



They're "hoisted" and only local to the function, though. I'd (personally) prefer it if it worked like C, and a variable was defined from the "var" statement until the next closing brace. Then you could have a variable local to one branch of an if statement, etc, without faking scopes by abusing closures (eg (function() { })(); ).

And nested loops that both use "i" would work.


"I'd (personally) prefer it if it worked like C, and a variable was defined from the "var" statement until the next closing brace."

It does work like C. The scoping you're talking about was introduced in C99.


Spidermonkey (or whatever they are calling it nowadays) supports this using the "let" keyword, fwiw.


Try this: for ( var i = 1; i < 10; ++i ) setTimeout( function( ) { alert( i ) }, i * 250 )

You might expect it to alert 1..2..3..etc. It actually alerts 10..10..10..etc.


That's because closures actually capture the variable, not the current value of it. They'd be a lot less useful if they just made a copy of the variables...


I'm sorry my original post wasn't very clear.

My intended point was that if Javascript had true lexical scope then each iteration of the loop would create a distinct variable. A reference to that variable would then be captured by each closure. You can simulate the behavior of lexical scoping by "abusing" closures as someone mentioned before. Like this:

for ( var i = 1; i < 10; ++i ) ( function( i ) { setTimeout( function( ) { alert( i ) }, i * 250 ) } )( i )


> You can simulate the behavior of lexical scoping by "abusing" closures as someone mentioned before.

I wonder about the word "abusing" here. Languages that have block-level lexical scoping (I hope I am using the correct word) sometimes implement block scoping by renaming the variables and hoisting them to the enclosing function, and they sometimes implement block scoping by creating a new closure just as we do by hand in JavaScript, and then hoisting the result.

For example, naïve implementations of Scheme give you a "let" macro that is expanded into the closure form you give above, and then an optimizing step comes along a little later and performs lambda hoisting for you. So this "abusing" we are doing is what the language would have done for us in certain implementations anyways!


For the record, you can also create scope without a closure using with (or let, if it's available.)

  for (var i = 1; i < 10; ++i)
  {
    with({i:i}) // let (i=i)
    {
       setTimeout( function( ) { alert( i ) }, i * 250 );
    }
  }
will do what you intend. If let is available, for (let i = 1; etc. will do the same thing without the cruft.


That isn't lack of lexical scope. It's that the for() syntax isn't purely functional in how it binds variables. One could define an equivalent syntax in Scheme and see the same result.

EDIT: http://pastebin.com/7BUXKVXT

Paste it into Racket and choose language R5RS.

  (define-syntax for
    (syntax-rules ()
      ((for ((var init) condition step) expr ...)
       (let ((var init))
         (let loop ()
           (if condition
               (begin
                 (begin expr ...)
                 step
                 (loop))))))))

  (define (test-for)
    (let ((v (make-vector 10)))
      (for ((i 0) (< i 10) (set! i (+ 1 i)))
        (vector-set! v i (lambda () i)))
      (for ((i 0) (< i 10) (set! i (+ 1 i)))
        (display ((vector-ref v i)))
        (newline))))


That was me who mentioned it, actually :-) Yes, I see what you mean now, sorry!

This reminds me of bug in the Visual C++ 6.0 compiler, where a variable declared in the "head" of a for loop would continue to exist after the loop scope had closed. Javascript has exactly the same problem, but by design, due to only having global and function-level scope.


This is completely wrong. I don't know what I was thinking.

It would be the same variable with or without lexical scope (by that I mean block-level).


Python does this as well.

Personally, I would like to be able to choose the capture method, but having value capture the default. Most of my code simply wants the value, but occasionally, capturing the variable is needed.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: