开发者

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:

  1. Call the function via an object property using property accessor notation, either dotted notation (obj.foo()) or bracketed notation (obj["foo"]()).
  2. 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)
  3. Use the 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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜