I saw the example below explained on this site and thought both answers would be 20 and not the 10 that is returned. He wrote that both the comma and assignment returns a value,
It may help to think of the dot operator as behaving similarly to a with
statement. When you run foo.bar()
, the result is much the same as if you ran:
with (foo) {
bar(); // returns 20
}
This will run your bar
function with foo
overlaid on top of the global object, allowing it to find the x
in foo
and thus return 20.
If you don't call bar
immediately, though, as in (foo.bar = foo.bar)
, you instead get:
with (foo) {
bar; // returns "bar" itself
}
The result is then passed out of the parentheses, producing an intermediate statement like <reference to bar>()
, which has no dot operator, so no with
statement, so no access to foo
, just to the global value of x
.
(The dot operator doesn't actually convert to a with
statement, of course, but the behavior is similar.)
You're misunderstanding it.
Both examples are returning the window
's x
property, since they aren't being directly invoked on foo
.
The value of the this
keyword inside a function depends on the context in which the function was called.
In an ordinary function call (eg, myFunc()
), this
will be the global object, which is usually window
.
In an object method call (eg, foo.bar()
), this
will be the object on which the function was invoked. (in this case, foo
)
You can set the context explicitly by calling myFunc.call(context, arg1, arg2)
or myFunc.apply(context, argArray)
.
Both of your examples are normal invocations of an expression that evaluates to foo.bar
.
Therefore, this
is the window
.
They are equivalent to
var func = (some expression);
func();
It doesn't have to do with values vs. references, it has to do with this
values (as you suspected). In JavaScript, this
is set entirely by how a function is called, not where it's defined. You set the this
value in one of three ways:
obj.foo()
) or bracketed notation (obj["foo"]()
).with
statement (really just a variant of #1, but worth calling out separately, particularly as it's not obvious from the source code)apply
or call
features of the function instance.In your examples above, you're not doing any of those, so you end up calling the function with the default this
value, the global object, and so x
comes from there rather than from your foo
object. Here's another way to think about what that code is doing:
var f = foo.bar; // Not calling it, getting a reference to it
f(); // Calls the function with `this` referencing the global object
If you don't directly use a property to actually make the call (instead retrieving the value of the property and then making the call with that), the this
handling doesn't kick in.
You should understand how the internal Reference Type works.
Note: This is not a language data type, is an internal mechanism to handle references.
A reference is composed of two elements, the base object and a property name.
In your example, the foo.bar
reference looks like this.
// pseudo-code
foo.bar = {
baseObject: foo,
propertyName: 'bar'
}
Both, the comma operator and a simple assignment, rely on getting the value of the property name, that causes the base object to be lost, since a single value is returned (this is made through the internal GetValue operation).
This is how the internal GetValue
operation works:
// pseudo-code
GetValue(V) :
if (Type(V) != Reference) return V;
baseObject = GetBase(V); // in your example foo
if (baseObject === null) throw ReferenceError;
return baseObject.[[Get]](GetPropertyName(V));
// equivalent to baseObject[v.PropertyName];
As you see, a value is returned, so the original reference is lost.
Edit: The key to understand why (foo.bar = foo.bar)();
is not equivalent to foo.bar();
relies in the Simple Assignment Operator, let's see the algorithm:
11.13.1 Simple Assignment (`=`) The production `AssignmentExpression` : `LeftHandSideExpression` = `AssignmentExpression` is evaluated as follows: 1. Evaluate LeftHandSideExpression. 2. Evaluate AssignmentExpression. 3.Call GetValue(Result(2)). 4.Call PutValue(Result(1), Result(3)). 5.Return Result(3).
Basically when you make (foo.bar = foo.bar)
the actual assignment (Step 4.) has no effect because PutValue
will only get the value of the reference and will place it back, with the same base object.
The key is that the assignment operator returns (Step 5) the value obtained in the Step 3 and as I said before in the GetValue
pseudo-code, this internal method returns a value which doesn't really have a base object.