Inner functions and outer 'this' (Re: That hash symbol)

Mike Samuel mikesamuel at gmail.com
Sat Mar 26 10:24:55 PDT 2011


2011/3/26 Claus Reinke <claus.reinke at talk21.com>:
>> We spent time yesterday at the TC39 meeting not only on shorter
>> syntax but exactly how to support better |this| handling for several
>> distinct use-cases: inner functions that want the outer |this|,
>> callbacks that want a certain |this|, and object methods that
>> want the receiver when called as methods of a given (receiver)
>> object (else possibly a default such as the outer function's |this|).
>
> That reminds me of an old solution looking for this problem.
>
> Back in 1976, Klaus Berkling suggested to complement the
> lambda calculus with an operator to protect variables from
> the nearest enclosing binding [1].
>
> The idea is simply that (lexically scoped) variables usually
> are bound to the next enclosing binding of the same name,
> while protected (lexically scoped) variables are bound to
> the next _outer_ enclosing binding of the same name
> (each protection key skips one level of binding, lexically).
>
> If I may use '#' as a placeholder for a suitable protection
> key, then this translates to Javascript as
>
> function Outer() {
>   var x = "outer";
>   function Inner() {
>       var x = "inner";
>
>       log(x); // "inner"
>       log(#x); // "outer"
>       log(##x); // global scope, probably unbound
>   }
> }


To clarify, if I have the code

   foo(#x)

and suddenly I realize that I need a to guard it with a condition,

  if (bar) {
    foo(#x)
  }

this would ?not? change the binding of #x since, although I have
introduced a new let scoped block, x is not declared in that scope so
it is not counted against the stack of #'s.


It is not always easy to count scopes though without knowing a lot of
details.  E.g. changing

function f() {}

(function () {
   function () {
     return f;
   }
})()

requires two hashes

function f() {}

(function () {
   function f() {
     return ##f;
   }
})()

since f was introduced both into the body scope, and into the outer
scope via the function declaration.


It seems brittle, and it seems that overloading labels to name scopes
would provide a less brittle alternative if this really is desired.

myScopeName: {
  let x;

  function f(x) {
     g(x + myScopeName.x);
  }
}


More information about the es-discuss mailing list