Why does the assignment operator return a value and not a reference?

后端 未结 4 1653
挽巷
挽巷 2021-02-04 12:04

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,

相关标签:
4条回答
  • 2021-02-04 12:05

    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 讨论(0)
  • 2021-02-04 12:15

    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();
    
    0 讨论(0)
  • 2021-02-04 12:16

    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.

    0 讨论(0)
  • 2021-02-04 12:16

    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.

    0 讨论(0)
提交回复
热议问题