Capture this!

JavaScript woes:

  1. function C() {
  2.     var _this = this;
  3.     this.foo = function () { return _this === this; };
  4.     this.bar = function () { return this.foo; };
  5.     return this;
  6. }
  7. var c = new C();

What is the value of c.foo()? What is the value of c.bar()()? Both are true. But let:

  1. var foo = c.bar()

What is the value of foo()?

If you thought it was true, you’re wrong. This doesn’t make much sense at all. Either this is captured by a dot, or it isn’t. Looking at the inner call to c.bar, it’s syntactically obvious that the call to bar has this = c. But why is that preserved when evaluating it within an outer expression, but not when we split the expression into two statements?

I’m sure this has been seen before, but I’ll probably show it to Dave Herman anyway. If I remember correctly, Python gets this right. Between this and a prototype aliasing bug I had yesterday, I’m irritated.

As an aside, I was speaking with Arjun Guha last night, and none of the conventional type theory would have helped me with these problems. In fact, I get almost no type errors in JavaScript — only grotesque object model problems.

* Filed by Michael Greenberg on 2007-03-14 at 2:09pm under Programming Languages

a weasel in a hat

9 Responses to “Capture this!”

  1. April 17th, 2007 | 10:50am

    OpenLaszlo gets this wronger (but perhaps more consistently):

    c.foo() true
    c.bar()() false
    foo() false

  2. April 17th, 2007 | 11:36am

    Are you sure you’re running in the latest Firefox? Dave Herman said that it’s an EC4 feature; it might only be in the latest versions of Spidermonkey.

  3. April 18th, 2007 | 7:31am

    I compiled your test code to ActionScript with the OpenLaszlo compiler - the results are actually from Flash 8, though I’ll have to track down the code our compiler emits to see whose fault it is.

  4. April 18th, 2007 | 1:52pm

    The OL compiler doesn’t touch that bit of script, so the results I gave in the first comment are how ActionScript behaves. So c.bar() returns a function closed over a different `this`?

  5. April 27th, 2007 | 1:40pm

    I’m looking at this again and I’m confused now. What is the problem with this example? All three major browsers agree on the behavior.

    The binding of `this’ in JS is determined by the syntax of the function call. When you said `c.bar()’ you invoked `bar’ as a method of `c’, so it bound `this’ to `c’ for the evaluation of the body of `bar’. But when you then called `foo’ as a naked function, it bound `this’ to the global object.

    That’s the stated, specified behavior of JavaScript. Are you claiming there’s an inconsistency between browsers/implementations of JS, or just that the specified behavior is weird? (If it’s the latter, you’ll get no disagreement from me.)

  6. April 27th, 2007 | 1:42pm

    D’oh! I missed the part where they disagree: `foo.bar()()’. Sorry I posted too soon before I read more carefully.

    I still need to double-check where this divergence in behavior comes from.

  7. April 27th, 2007 | 2:50pm

    I think I found it. Look at

    http://developer.mozilla.org/es4/proposals/bug_fixes.html

    and search for “[THIS.PROPAGATES]“.

  8. April 28th, 2007 | 4:38pm

    Okay, it turns out I was wrong and this has nothing to do with the new behavior of `this’ in ES4 — Mozilla has not implemented that yet.

    Brendan Eich informs me that it’s a bug that’s been fixed in alpha versions of Firefox/SpiderMonkey:

    “[this example] changed behavior from Firefox 2 (the mozilla 1.8 branch) to Minefield (alpha Firefox 3, the 1.9 cvs trunk). My trunk js shell gives false, my 1.8 branch shell gives true in agreement with Firefox 2.0.0.x.

    This looks like a bug fix due to Igor’s work in https://bugzilla.mozilla.org/show_bug.cgi?id=363530.”

    He’s referring to `c.bar()()’ returning true or false, BTW. It should return `false’.

  9. April 28th, 2007 | 11:15pm

    Ah. That’s a relief that it’s a bug. I think the behavior itself is weird, and that dotting should either always or never capture this.

    Thanks for looking back into this!

Leave a reply

Please submit only once. Comments are moderated and will not appear immediately.