Closed Bug 469609 Opened 16 years ago Closed 9 years ago

meaning of "this" in lambda expression enclosed by method is unexpected

Categories

(Core :: JavaScript Engine, defect)

defect
Not set
normal

Tracking

()

RESOLVED WONTFIX

People

(Reporter: myk, Unassigned)

Details

The following code, in which a lambda expression references "this", evaluates to 3, as expected:

  var foo = { 
    a: 1, 
    b: function(c) this.a + c
  };
  foo.b(2);

This code, however, which also contains a lambda that references "this", evaluates to NaN:

  var foo = { 
    a: 1,
    b: function(c) { 
      var d = function() this.a + c;
      return d(c);
    }
  };
  foo.b(2);

I expected "this" in the second example to refer to the enclosing method's "this" so that the second example would also evaluate to 3.

I can work around the problem in that example fairly easily:

  var foo = {
    a: 1,
    b: function(c) {
      var d = function() this.a + c;
      return d.call(this, c);
    }
  };
  foo.b(2);

However, that doesn't work in this example, which is similar to the real-world use case in which I first noticed the issue:

  var foo = {
    a: 1,
    b: function(ary) {
      return ary.filter(function(obj) obj.a == this.a);
    }
  }
  foo.b([objects...]);

To make that example work, I have to do:

  var foo = {
    a: 1,
    b: function(ary) {
      let a = this.a;
      return ary.filter(function(item) item.a == a);
    }
  }
  foo.b([objects...]);

That seems clunky (just as it is when assigning "this" to a local variable to access it from an event handler closure).

I suppose my expectations are shaped by the lack of explicit block delimiters in the lambda, while the current behavior is the result of Spidermonkey treating it as a regular function with implicit block delimiters and function invocation semantics.

Unless there's some advantage to the current behavior, though, it'd be useful to treat the lambda specially, giving it the enclosing method's scope (modulo those that the lambda's parameters mask) so that "this" in the lambda would refer to the enclosing method's "this".
It would be nice if 'this' were a lexically scoped variable when unbound (when not forced to a certain object binding). But ES3 tried too hard to censor the activation object and ended up making a global object leak instead.

Hard to fix compatibly, outside of a "strict mode". Even crazier, objcap folks have argued against lexical 'this', even though they're otherwise in favor of lexical scope. See

https://mail.mozilla.org/pipermail/es-discuss/2008-August/007317.html

My opinion is that the example there comes with a "caveat lexical 'this' emptor" label, and works as designed (in the dream-world where unbound 'this' is lexically scoped).

/be
(In reply to comment #1)
> My opinion is that the example there comes with a "caveat lexical 'this'
> emptor" label, and works as designed (in the dream-world where unbound 'this'
> is lexically scoped).

Fair enough.  And I just remembered I can work around the issue for array extras like 'filter' by explicitly passing the 'this' object into them, i.e. |ary.filter(function(obj) obj.a == this.a, this)|, which is simple enough.
Assignee: general → nobody
ES2015 added Arrow functions [1] for this use case. Also expression closures are going to be removed (bug 1083458). Therefore resolving as Won't Fix.

[1] https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Status: NEW → RESOLVED
Closed: 9 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.