> That has me thinking. Do the closures really consume as much additional
> memory as the entire host scope's execution context, plus it's own, or just
> the added pointers to the variables it actually closes over (i.e. uses
> within it).

According to the spec, every execution context has a "variable
object", and the vars and parameters related to that context are
properties of that object.  (The "variable object" for the global
scope is the global object -- window, in our case -- but there's no
way to directly reference the ones created for each function.)  This
variable object is put at the top of the scope chain in effect for
that context, which is effectively a linked list of the variable
objects of each containing context.  When you create a new function
inside a context, the new function gets its own variable object linked
at the top of the containing context's scope chain.  So not a copy of
the containing context, and not pointers to individual vars that
actually get used, but a pointer to the variable object and associated
scope chain.

The memory issue comes into it because the closure keeps that entire
scope chain alive where (absent any closures) it would be eligible for
cleanup when the function exited.  The nice thing about bind, aside
from convenience, is that as kangax pointed out the variable object
and scope chain are quite thin.  You can happily bind without worrying
(much) about what you're keeping live.

So, scenario:

function init() {
    var container;

    container = $('container');
    hookUpHideables(container);
    container.show();

    function hookUpHideables(p) {
        p.select('.hideable').invoke('observe', 'click', function(evt)
{
            this.hide();
        });
    }
}

Perhaps the author was thinking:  "I don't need hookUpHideables()
except when init runs, and init() only runs once, so I'll let it get
reclaimed after init() ends."  hookUpHideables() doesn't do anything
with the 'container' var, nor does the anonymous closure it creates.
But they both keep 'container' alive nevertheless, indirectly via the
scope chain.  It doesn't matter for hookUpHideables() since no
reference to it survives the return of init(), and so it's eligible
for cleanup when init() returns.  But the anonymous closures survive,
and so the entire scope chain does, including the reference to the
host object inside 'container'.  (You can break the reference to the
host object by clearing 'container' before exiting init(), but the
scope chain lives on regardless.)

Although some optimizations are presumably possible at the
implementation level, I'd have to re-read the spec to see whether they
would violate it, and in any case exec() and such make life very
difficult for the implementers in terms of figuring out what may get
referenced at runtime.

If we recast our example to use a named function for the handler:

function init() {
    var container;

    container = $('container');
    hookUpHideables(container);
    container.show();

    function hookUpHideables(p) {
        p.select('.hideable').invoke('observe', 'click',
hideableClick);
    }
}

function hideableClick(evt) {
    this.hide();
}

...no closures have outstanding references at the end of init(), and
the chain is eligible for GC.

FWIW,
--
T.J. Crowder
tj / crowder software / com
Independent Software Engineer, consulting services available


On Mar 11, 1:39 am, Ryan Gahl <ryan.g...@gmail.com> wrote:
> > I guess what I wanted to say is that a function created with
> > Prototype's `bind` (internally) has a predictable closure - the one
> > that's got formed when a function was declared. And it is declared by
> > Prototype, inside a very "slim" scope (self-executing function,
> > surrounding `Function.prototype` extensions). That scope has no heavy
> > objects, no host objects (!) and so when bind internally creates a
> > function, that function closure consumes a relatively small amount of
> > memory.
>
> That has me thinking. Do the closures really consume as much additional
> memory as the entire host scope's execution context, plus it's own, or just
> the added pointers to the variables it actually closes over (i.e. uses
> within it). I.e., are the interpreters smart enough to create the new
> execution context in any sort of optimized manner?
>
> If not, then wouldn't it just not matter? If it duplicates the entire host
> context's memory footprint, then would that not lead all the way up to the
> main (window) context, presumably? Therefore, your statement of .bind()'s
> closure being "thin" isn't necessarily true, as it would derive from a
> function chain relative to where .bind() actually gets called anyway,
> ultimately closing over the entire global namespace (as do all functions).
> Also, does it really consume additional memory for that closed over context,
> or simply pass it in by reference, thus it being truly shared memory. Again,
> I would have to believe it's not a O(log n) operation... that would truly
> result in major memory consumption. My GUESS is, it's about a horse a piece
> (6 one way, half a dozen the other).
>
> Interesting. I wonder if there are tools around to profile a javascript
> context tree and see what's going on with this.
>
> At any rate... I used to be "bind happy", as I think most people are, but
> now implement a pattern that precludes having to use it almost ever. Really
> the only time I employ it these days is if I get lazy. The modified module
> pattern I described in that other thread really does away with the need to
> use it (i.e. create functions within the context in which they are needed,
> or pass the instance in)... I've become a bit more "functional" with my js
> these days.
>
> Also, RE: your comment about deeply nested function definitions being an
> indicator of poor design... it's often a good technique to actually _save_
> on resources (lazy definitions), wherein the memory for the function is not
> set aside until it's actually needed (assuming you use "var foo = function()
> {};" instead of "function foo() {}", as the latter results in memory
> allocation immediately with some interpreters). So it _can_ be good design.
>
> Ryan Gahl
> CEO
> Nth Penguin, LLChttp://www.nthpenguin.com
> --
> Inquire: 1-920-574-2218
> Blog:http://www.someElement.com
> LinkedIn Profile:http://www.linkedin.com/in/ryangahl
>
>
>
>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com
To unsubscribe from this group, send email to 
prototype-scriptaculous+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to