Hacker News new | ask | show | jobs
by ww520 5827 days ago
It has to do with Javascript's broken closure support that causes this kind of surprises in coding.

Traditional closure closes over the current variable values of a frame environment at each creation of an inner function instance. A new frame environment is cloned for the function instance with all the current variable values sealed. That's why it's called a closure; it's closed, sealed, that no one has access to it except the particular function instance.

Javascript cheats in its closure implementation. It doesn't create a new frame environment for each inner function instance, instead just references back to the parent's frame environment. Thus all inner function instances share the same frame environment. Any code in the parent function or in any function instance modifying a variable will instantly affect all the function instances. Closure has lost its meaning in Javascript. It should be called Shareture.

2 comments

It's not broken at all, and not in any way peculiar to javascript.

    >>> map(apply, [lambda:i for i in range(5)])
    
    [4, 4, 4, 4, 4]

    * (mapcar 'funcall (loop for i from 0 to 5 collect (lambda () i)))

    (6 6 6 6 6 6)
It's just that the looping constructs don't introduce a new binding, but modify the existing one.

    >>> map(apply, [lambda j=i:j for i in range(5)])
    
    [0, 1, 2, 3, 4]

    * (mapcar 'funcall (loop for i from 0 to 5 collect (let ((j i)) (lambda () j))))
    
    (0 1 2 3 4 5)
It's not broken, it's just the way it works in js. Maybe it should be called something else, but then maybe people should read the language specs better to see how things work in js...
Documenting it is how bug becomes feature. :)