<shaver> so when you make a closure in JS
<shaver> a new function object is (effectively) created every time the closure expression is evaluated
<shaver> function makeClosure(a) { return function (b) { return b + a; } }
<shaver> var f2 = makeClosure(1);
<shaver> var f3 = makeClosure(4);
<shaver> f2 and f3 have different scope chains
<shaver> each points to a different "activation" object, which has the argument and local-variable state for the invocation in which it was created
<shaver> (effectively, anyway -- the engine goes to some lengths to avoid creating such objects)
<bryner> ok...
<shaver> so f2's chain looks like: f2 -> makeClosure(1)'s scope -> global
<shaver> and f3's goes through the makeClosure(4) one
<bryner> so when are 1 and 4 evaluated?
<shaver> js> f2(10)
<shaver> 11
<shaver> js> f3(10)
<shaver> 14
<shaver> there's no lazy evaluation (lisp-style)
<bryner> so if instead of 1 or 4 i passed in "a", after saying "var a=5;" would that function object forever return x + 5, or would it always return x + a?
<shaver> x + 5
<bryner> ok
<shaver> basically, we snapshot the state of those variables
<bryner> but a would still be in the scope of the new function object?
<shaver> ask that question again
<bryner> supposing you change the letters around so that we don't have two a's
<shaver> function makeClosure(a) { return function (b) { return b + a; } }
<shaver> that has only one a
<bryner> function makeClosure(a) { return function (b) { return b + a; dump(x); } }
<bryner> var x = 3;
<bryner> var f2 = makeClosure(6);
<shaver> ah, OK
<bryner> so in that case, does x get evaluated when the closure is created?
<shaver> no
<shaver> let me back up a bit
<shaver> function f(a) { var v = 4; return v + a; }
<shaver> f(5)
<shaver> when we enter f, we create an "activation" object
<shaver> it holds the arguments and variables, so it starts with a = 5
<shaver> and v = undefined
<shaver> then v = 4 is evaluated, so we have { a:5, v:4 }
<shaver> the scope chain looks like { a:5, v:4 } -> global_scope_with_x_etc.
<shaver> so |v + a| finds both values in the first part of that, in the activation object
<bryner> ok
<shaver> if we have, instead:
<shaver> var x = 4;
<shaver> er
<shaver> var x = 42;
<shaver> function f(a) { var v = 4; return v + a + x; }
<shaver> then it will find v = 4 and a = 5 in the activation object, but it'll have to walk out to the global object to find x
<shaver> (returning 51, natch)
<shaver> when f finishes, the activation object goes away
<bryner> ok
<shaver> so back to our closure case
<shaver> js> function makeClosure(a) { return function (b) { return b + a + x; } }
<shaver> js> var f2 = makeClosure(1);
<shaver> when we enter makeClosure(1), scope chain looks like { a:1 } -> global
<shaver> that scope chain is "saved" by the closure we create (that exact object; if we save it in multiple closures, they will share an outer scope, and see each other's changes!)
<shaver> so now we call
<shaver> js> f2(10)
<shaver> when we enter f2, an activation object is created for it, and put on the head of the saved scope chain
<shaver> so now we have
<shaver> { b : 10 } -> { a : 1 } -> global({x : 3})
<bryner> ok
<shaver> the lookup finds b in the current activation object, a in the saved one from makeClosure's invocation, and x in the global scope
<shaver> all of those are looked up at that point
<bryner> got ti
<bryner> it
<bryner> so now, what happens when you use "this"
<shaver> what bit you is that |this| doesn't use scope-chain lookup
<shaver> it's not a variable name
<shaver> it means "the object on which this method was invoked, or the global object if none"
<shaver> basically
<shaver> ECMA and our engine disagree, basically because ECMA doesn't really cope with a world where there are multiple global objects
<shaver> but that's neither here nor there
<bryner> if i did:
<bryner> var myFunction = this.doFoo();
<bryner> myFunction();
<bryner> would "this" be the global object inside of the function?
<shaver> yes
<shaver> (or the static scope-parent of doFoo, I forget -- usually they're the same)
<bryner> but if i just do "this.doFoo();", this is the object that contains the doFoo function
<shaver> don't use "this" like that
<shaver> if you do "obj.doFoo()", "this" is "obj"
<bryner> er, yeah
<shaver> we don't care about the property relationship, really
<shaver> you could also say doFoo.apply(obj)
<shaver> which is the same thing
<bryner> oh, interesting
<shaver> or doFoo.call(obj)
<shaver> (apply and call are similar: one takes an arguments array, the other is variadic)
<bryner> those actually work? i've never seen them used
<shaver> yeah, I use them
<bryner> cool
<bryner> that all actually makes sense
<shaver> function f(a, b, c) { dump([].join.call(arguments, ", ")); }
<shaver> rather:
<shaver> js> function f(a, b, c) { print([].join.call(arguments, ", ")); }
<shaver> js> f(1, 2, "foo");
<shaver> ([].join is just shorter than Array.prototype.join)
<shaver> so you understand why
<shaver> var This = this;
<shaver> elem.addEventListener("foo", function() { This.doFoo(); });
<shaver> works?
<bryner> yes
<bryner> need to go eat now
<bryner> thanks for the help
<shaver> one sec
<shaver> I get to show you something else
<bryner> ?
May 07 20:49:50 --- Offering closure-sharing.js to bryner
<shaver> : ds; jsshell -f /tmp/closure-sharing.js
<shaver> foo
<shaver> bar
<shaver> ; ds;
<shaver> anonymous, unreachable storage!
<shaver> shared by two function objects!
<shaver> now, go eat =)
<shaver> (actually, that's not quite true:
<shaver> js> Set.__parent__.shared
<shaver> bar
<shaver> js> Set.__parent__.shared = 42;
<shaver> 42
<shaver> js> Get()
<shaver> 42
<shaver> because we expose the scope chain through __parent__)
<shaver> anyway, I'm going to go get a beer
<shaver> have fun