var obj = {};
var r1 = (obj[\'toString\'])();
var m1 = obj[\'toString\'];
var r2 = m1();
var r3 = (obj.toString)();
var m2 = obj.toString;
var r4 = m2();
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.
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
andtypeof
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.
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:
- Let
ref
be the result of evaluatingMemberExpression
. which may return aReference
- Let
func
be GetValue(ref)
. which fetches the actual function object - you see this operation a lot in the spec- … fetch arguments, do some type assertions
- If Type(
ref
) isReference
, then
- If IsPropertyReference(
ref
) is true, then
LetthisValue
be GetBase(ref
). <- here the method context is fetched- Else, the base of
ref
is an Environment Record
… which basically describes variables inside awith
statement- Else, Type(
ref
) is notReference
.
LetthisValue
beundefined
.