37
var a = 1; var b = { a : 2, c : function () { console.log(this.a); } }; b.c(); // logs 2 (b.c)(); // logs 2 (0, b.c)(); // logs 1 

The first is understandable, for "this" is pointed to Object "b". But why does the second one log the same result? I thought "this" should be pointed to the global execution context. And the third one, it seems that the comma operator influences the execution context.

5
  • this is not an "execution context." Commented Mar 18, 2016 at 8:53
  • 1
    @Crowder Could you please explain more clearly? What I find in ECMAScript® Language Specification is "The value associated with the this keyword within ECMAScript code associated with this execution context." Commented Mar 18, 2016 at 10:32
  • 1
    Execution contexts have a this, but the this is not the execution context, it's a property of it. More specifically, the this binding is a property of the execution context's lexical environment (or the this binding of an enclosing context's lexical environment, if the execution context's lexical environment doesn't have one, as happens with ES2015 arrow functions). §8.3 is a good place to start. Warning: The 5th edition spec was turgid, but the 2015 one is even worse. :-) Commented Mar 18, 2016 at 11:14
  • 1
    possible duplicate of What's the reason for using such syntax: (0, _.Em)() Commented Sep 16, 2016 at 12:29
  • Possible duplicate of What's the reason for using such syntax (0, _.Em)(); Commented Mar 22, 2017 at 12:54

2 Answers 2

23

You really have a nice corner case there! My take on it:

  • the first is straightforward. Just a standard call. The '.' operator lets you call the function setting b as the execution context.
  • the second is exactly the same thing: the parens are entirely optional and the interpreter is treating the expression inside it as a bound function call. Actually I didn't expect this: I thought the interpreter would be going to reset this to the global object, but actually it's keeping it linked. Probably just so "casual" language users do not freak out.
  • the third one is more standard (at least for those who live in JavaScript land): as soon as your function is passed on in an expression (in this case by the , operator) the this value is lost. This is because b.c is a Property Reference (deep rabbit hole details in the specification, here, courtesy of T.J.Crowder). So, you are actually passing around the function itself, no more bound to the declaring object. So when you call it this is going to be passed in as the global object.

See it this way: (object.function)() gets simplyfied into object.function(), because the enclosing parens are completely optional; (0, object.function)() is parsed as (expression yielding a function)() which is going to lose the object binding to this, because function is already unbound.

Really nice example!

Sign up to request clarification or add additional context in comments.

1 Comment

Good update, although "as soon as your function is passed on in an expression (in this case by the , operator) the this value bound to the execution context is lost" is still incorrect: We're not using this, we're using b, and the mechanism by which the object reference doesn't end up being used is because the property reference isn't directly used to make the call. Details in §12.3.4.1.
20

Refer to Indirect eval call, which gives more details about it.

 ( 0 , b.c ) ( ) |____| |_____| |_____| Literal Operator Identifier |_________________________| Expression |______________________________| PrimaryExpression |______________________________| |________| MemberExpression Arguments |________________________________________________| CallExpression 

We can use the comma operator to fashion an indirect call to b.c which will force it to execute in the global context, the value of a is 1 in the global context.

Also the result of (b.c = b.c)() is 1

> (b.c = b.c)() 1 

Speaking in terms of ECMAScript, this is because both — comma operator (in (0, b.c) example) and = operator (in (b.c = b.c) example) perform GetValue on its operands.

Other indirect call formats as below

> (b.c, b.c)() 1 > (1? b.c: 0)() 1 > (__ = b.c)() 1 

1 Comment

Operator Comma calls operator "=" (or perform GetValue) and because of this the property this of execution context is changed. Thank you for sharing your knowledge! 🙏

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.