Capture this!

JavaScript woes:

function C() {
    var _this = this;
    this.foo = function () { return _this === this; }; 
    this.bar = function () { return this.foo; };
    return this;
}
var c = new C();

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

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.

9 Comments

  1. 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.

  2. 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`?

  3. 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.)

  4. 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.

  5. 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’.

  6. 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

Your email address will not be published. Required fields are marked *