4
var obj = {};

var r1 = (obj['toString'])();
var m1 = obj['toString'];
var r2 = m1();

var r3 = (obj.toString)();
var m2 = obj.toString;
var r4 = m2();

r1 and r3 expectedly contain correct result: "[object Object]", while r2 and r4 contain "[object Undefined]" showing that m1 and m2 are not bound to object.

I can't fully comprehend how obj['toString']() is executed. I always looked this way, (obj['toString'])() -> (function obj)(). Turns out that function invocation operator looks back on what is the context. I would expect operator to not know where operands come from.

Can anyone properly explain this behavior?

Andrey
  • 59,039
  • 12
  • 119
  • 163

3 Answers3

4

Turns out that function invocation operator looks back on what is the context. I would expect operator to not know where operands come from.

In fact, it does know.

In the EcmaScript specification this is described by the property accessor operators (and a few similar operations) to return a "Reference object" that holds exactly this information: the context on which the property was accessed. Any "normal" operations will usually just get the reference's value - including the assignment operators, which do dissolve the reference in your case.

The call operator uses this to make method calls special:

  1. Let ref be the result of evaluating MemberExpression. which may return a Reference
  2. Let func be GetValue(ref). which fetches the actual function object - you see this operation a lot in the spec
  3. fetch arguments, do some type assertions
  4. If Type(ref) is Reference, then
    • If IsPropertyReference(ref) is true, then
      Let thisValue be GetBase(ref). <- here the method context is fetched
    • Else, the base of ref is an Environment Record
      which basically describes variables inside a with statement
  5. Else, Type(ref) is not Reference.
    Let thisValue be undefined.
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • So this Reference object is implicitly converted to just function object if not invoked in place? – Andrey Sep 01 '14 at 16:49
  • 2
    Yes, this happens in the assignment operator. It doesn't happen in the grouping operator (parenthesis) for example. – Bergi Sep 01 '14 at 16:49
4

This is actually a "special" behavior of the grouping operator (...):

1. Return the result of evaluating Expression. This may be of type Reference.

NOTE This algorithm does not apply GetValue to the result of evaluating Expression. The principal motivation for this is so that operators such as delete and typeof may be applied to parenthesised expressions.

So, this operator specifically does not call GetValue and thus does not return the function object itself but rather the whole reference, so that operations which expect a reference still work.


A Reference is basically an encapsulation of a value with an optional "base value" which is the object in case of a property access.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
0

The meaning of "this" always seems to cause confusion in JavaScript, especially in scenarios such as callbacks.

Modifying your example slightly, as you are using toString() and we can't see its implementation, I've introduced a function that can be seen to depend on the context, that is the meaning of this. The coder is expecting to access the object attribute x, but he gets surprised when the function is unbound.

window.x = "some global value";
var obj = { 
    wibble : function() { console.log("wibble " + this.x); }, 
    x : 7
};

obj.wibble();
var f = obj.wibble;
f();

This results in:

wibble 7 
wibble some global value

That is when we explictly specify a context in the form obj.wibble() or indeed obj["wibble"], this behaves as most developers expect.

However when we call the "naked" function, we are effectively getting a default context, and in my example I happen to have a value for x in that context and so get that value, which happens to be a string.

djna
  • 54,992
  • 14
  • 74
  • 117
  • 1
    I don't think this question is specifically about `this`, but rather why/when the "connection" between the object and the function is lost. – Felix Kling Sep 01 '14 at 16:52