1

I'm implementing a JavaScript interpreter, and I can't figure out the details of binding functions to objects in JavaScript.

A rudimentary example:

const o = {
  x: 1,
  getX: function() {
    return this.x;
  }
};
o.getX(); // returns 1

The tricky part is what happens when you assign getX to a variable:

let gX = o.getX;
gX(); // returns undefined

My question is: how does the runtime know that o.getX() gets bound to o, but gX() should be unbound? I would assume gX and o.getX are pointing to the exact same function!

I first thought that maybe the presence of the . is what makes the difference. So, in the grammar, there's a production like <method-call> ::= <expr> '.' ID '(' <expr>* ')', and this expression is treated differently (the result of evaluating the first <expr> is bound to the function found under ID) than a "regular" call (without a .).

But the following expression seems to disprove this theory, because (o.getX)() also returns 1. However, in some magical way, (gX = o.getX)() returns undefined, even though it's clear to me the assignment expression returns its right-hand size, so o.getX in this case!

Is there a simple explanation of how these semantics are implemented? I can't figure out a way that my runtime is supposed to differentiate that o.getX is bound to o, but gX, even though it's pointing at o.getX, is not bound.

5
  • 2
    When talking about functions defined with function keyword, a simple rule is, that this is always bound when the function is called, without exceptions (sometimes it's a bit hidden where exactly a function is actually called). See the standard for more details. Commented Jan 26, 2023 at 7:47
  • 1
    "My question is: how does the runtime know that o.getX() gets bound to o, but gX() should be unbound? ... I first thought that maybe the presence of the . is what makes the difference." Not the . per se (it works with bracket notation as well), but yes, the fact that the function call is happening on the immediate result of a property access operation is what tells the JavaScript engine what value to use as this when calling the function. Some of the answers to the linked questions go into the technical detail, but: If we start with ... Commented Jan 26, 2023 at 8:17
  • 1
    ... property accessors, you can see that the result (via EvaluatePropertyAccessWithIdentifierKey) of MemberExpression . IdentifierName (for example) is a Reference Record that ... Commented Jan 26, 2023 at 8:18
  • 1
    ... includes the object resulting from MemberExpression as its [[Base]] value. That Reference Record is used when doing the function call and eventually in EvaluateCall, where it gets the thisValue to use from the Reference Record's [[Base]]. ... Commented Jan 26, 2023 at 8:18
  • 1
    ... When just doing gX(), there is no property expression, and the ref used with EvaluateCall isn't a Reference Record, so the thisValue used is undefined. (The [[Call]] internal method of the function may convert undefined to globalThis if it's a loose-mode function; and of course, arrow functions ignore the this they're called with&nbsp;&mdash; but those are different topics.) Commented Jan 26, 2023 at 8:18

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.