Why does the assignment operator return a value and not a reference?
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, not a reference. I don't quite understand what that means.
I understand it in relation to passing variables into functions or methods i.e primitive types are passed in by value and objects by reference but I'm not sure how it applies in this case.
I also understand about context and the value of 'this' (after help from stackoverflow) but I thought in both cases I would still be invoking it as a method, foo.bar() which would mean foo is the context but it seems both result in a function call bar().
Why is that and what does it all mean?
var x = 10;
var foo = {
x: 20,
bar: function () {return this.x;}
};
(foo.bar = foo.bar)();//ret开发者_如何学编程urns 10
(foo.bar, foo.bar)();//returns 10
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:
- Call the function via an object property using property accessor notation, either dotted notation (
obj.foo()
) or bracketed notation (obj["foo"]()
). - Call the function via an object property using a
with
statement (really just a variant of #1, but worth calling out separately, particularly as it's not obvious from the source code) - Use the
apply
orcall
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.
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 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.)
精彩评论