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

Kam Kasravi kamkasravi at yahoo.com
Tue Mar 29 00:43:39 PDT 2011



On Mar 28, 2011, at 10:35 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

> On Mar 27, 2011, at 11:13 AM, David Herman wrote:
> 
>> To be fair, your suggestion is more moderate than de Bruijn, although it's not clear whether you're proposing the ability to refer to shadowed bindings of *all* variables or just |this|. If it's the former, I'm *strongly* opposed. If it's the latter, well, I guess I'm still pretty opposed, just maybe less strongly. :)
> 
> 
> Dave, I think that applying this solution to the this-scoping issue may be a really good idea
> 
> Claus, thanks from bringing a new (old) idea into the discussion.
> 
> I agree with Dave about all the fragility issues he mentioned for the general case.  Also, I just don't see the general problem (if it is even a "problem") as one that is important enough to try to "fix" in the language  since it is probably rare and  it can be avoided by careful naming.
> 
> However, the specific case of 'this' is a different matter.    'this' is implicitly rebound in every function scope and the JavaScript programmer has no direct control over the naming and shadowing.  The result is that they have to know and use the self renaming pattern.
> 
> This issue shows up enough in defining objects via object literal methods and in methods that call higher order functions that it is something that may well be worth addressing in the language.  Particularly as we discuss adding additional declarative forms to Harmony that included nested method definitions.  The outer 'this' problem is limited enough that we can avoid things like dynamic scoping complications in the solution.  Also it is limited enough that I don't believe that the solution imposes refactoring complications.  
> 
> Here is a sketch of a proposal:
> 
> ^this is added as a new lexical token of the language. When spoken it is pronounced as "outer this" .  In the expression grammar ^this is a primary expression.
> 
> It is a syntax error for ^this to appear outside of a function body.  It may not occur at the top level of a program.  
> 
> When evaluated, the value of ^this is the this binding of the function that immediately lexically encloses the function body that contains the ^this.  It is a early syntax error if there is no such function.  For example:
> 
> //at the top level
> var self = ^this;  //syntax error, at the top level
> function foo() {
>   ^this.bar();  //syntax error, no enclosing function
> }
> 
> The two primary  use cases for ^this are exemplified by the following two examples:
> 
> MyObj.prototype.addClickHandingForElement(elem) {
>   elem.addEventListener('click', function (e) {^this.handleClick(this,e)});
> }
This does provide an elegant solution but perhaps for a somewhat uncommon problem. Today I think most would do the following (if not using self):
MyObj.prototype.addClickHandingForElement(elem) {
elem.addEventListener('click', this.handleClick.bind(this))
}
Where the event.target would be used in the context of handleclick. I've noticed that coffescript uses this=> to autobind but not disambiguate. One possible alternative would be to have a keyword modifier similar to your suggested method called "callback" which autobinds this. From a declarative viewpoint it would be clear what methods within the type were intended to be used in this way. Although it would not handle the inner outer this use case. Your second example below I would of done as:

MyObj.prototype.wrap = (function (wrapper) {
  // create a wrapper object that limits access to the properties of one of my objects
  return {
       name: this.id,  //fix name of wrapper at creation (uses this of wrap call
       get foo () {return wrapper.foo},  //outer this is this of wrap call
      set bar(val) {wrapper.bar = val}  //outer this is this of wrap call  
})(this);

even if ^this were available since I would find it clearer. At least in my experience I do not run into many scenarios where both this and ^this are accessed in the same context, but I do use/find many scenarios where callbacks should be bound especially when custom events are used liberally or within node. In those cases where I need both I tend to curry the inner this and provide it as a parameter to the outer callback. Somewhat contrived for the first example since elem is available in the event but it would look like
elem.addEventListener('click', this.handleClick.bind(this).curry(elem))
assuming curry was available on the Function prototype.


> 
> MyObj.prototype.wrap = function () {
>   // create a wrapper object that limits access to the properties of one of my objects
>   return {
>        name: this.id,  //fix name of wrapper at creation (uses this of wrap call
>        get foo () {return ^this.foo},  //outer this is this of wrap call
>        set bar(val) {^this.bar = val}  //outer this is this of wrap call  
> }
> 
> Note that only one level outer this access is supported, ^^this would be a syntax error.  In the rare cases where somebody really needs deeper access to shadowed this binding then they can fall back to the self pattern. 
> 
> I see minimal refactoring hazards here as the outer scope reference is limited to one level, is  explicitly marked at the usage site, and only applies to this.
> 
> Possible reservations: 
> This use of ^ probably would preclude its use as a short form of the 'return' keyword (for those people who aren't in favor of adding implicit returns).
> It looks odd to old Smalltalk programmers.
> 
> Overall, I really like ^this as a narrow solution to a specific real usage problem. I'm interested in reactions and unless somebody thinks of something that seriously torpedoes it I will probably write it up as a strawman. 
> 
> Allen
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss


More information about the es-discuss mailing list