15

For obvious reasons, in JavaScript, the following two calls are different:

foo.bar();

var bar = foo.bar;
bar();

Namely, in the first call, this is the foo object. In the second, it's a reference to the global scope. However, the following example is a little less intuitive:

(foo.bar)();

I would expect it to operate the same way as the second example, but it actually operates the same as the first. That is, this references foo, not the global scope.

What are JavaScript's rules for deciding when to make a function call a "method call" and when to simply call the function without a particular this?

EDIT:

As Felix Kling points out in a comment, I'm wondering why the third example doesn't use the window context when theoretically it should simply retrieve the function and call it without the additional context. His example clarifies my question a little:

(true && foo.bar)(); // 'this' refers to the global scope
Community
  • 1
  • 1
Alexis King
  • 43,109
  • 15
  • 131
  • 205
  • Why would you expect the second one to not have `this === foo`? – Niet the Dark Absol Feb 23 '14 at 00:41
  • @NiettheDarkAbsol: Because the result of evaluating the grouping operator should be the function object? – Felix Kling Feb 23 '14 at 00:41
  • @NiettheDarkAbsol I suppose it probably makes some sense for it to be treated as a method call, especially since in other languages it might be required (I'm thinking something like `((CastClass) obj).method()`, but in JS I'd think it would have the other semantics. – Alexis King Feb 23 '14 at 00:43
  • Just give some more context what the points is of this question: Given, `(true && foo.bar)()` `this` will refer to `window`. – Felix Kling Feb 23 '14 at 00:47
  • @Qantas94Heavy: Good find. Also CMS is much better at explaining than I am. It's interesting to see that there have been more qualitative answers answers than today. – Felix Kling Feb 23 '14 at 01:43
  • @FelixKling: this duplicate wasn't the easiest to find :) – Qantas 94 Heavy Feb 23 '14 at 01:44

2 Answers2

9

That's a tricky one and boils down to the inner workings of the ECMAScript standard. The definition of the grouping operator is:

The production PrimaryExpression : ( Expression ) is evaluated as follows:

  1. Return the result of evaluating Expression. This may be of type Reference.

With the added note:

This algorithm does not apply GetValue to the result of evaluating Expression. The principal motivation for this is so that operators such as delete and typeof may be applied to parenthesised expressions.

So this is the key: The result can be of type Reference. A Reference is an internal data type which consists of a base value and a referenced name.

For example evaluating the member expression foo.bar, results in a Reference with base value foo (the object) and the referenced name "bar" (simply a string representation of the identifier).

GetValue(ref) is the internal function which actually accesses the property of the object and returns the value of the property (the function object in this examples). Most operators call GetValue on their operands do resolve these References, but not the grouping operator.


Looking at how CallExpressions are evaluated might also give an idea of how this and References work. For example, one step is:

Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).

So, if you have a Reference value and try to call it, the value of this will be set to the base value of the Reference (foo in the above example).


Regarding my example (true && foo.bar)();: The && operator calls GetValue() on both of its operands, so the result of the grouping operator is not a Reference.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
-5

Anything of the form foo.bar() is calling the bar method of the foo object.

However in the case of var bar = foo.bar; bar();, you are creating a reference to the method and then calling the method with no context - hence it is given the default global context.

You can (except in old IE) do this: var bar = foo.bar.bind(foo); bar(); to have the function use the foo context.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • 5
    This doesn't actually answer the question. `(foo.bar)()` isn't of the form `foo.bar()`, so why does it get `foo` as `this`? – user2357112 Feb 23 '14 at 00:45