This

What the fuck is this?!


How

this value for a function is determined by how the function are called.

Objects

In the example below this will point to globalThis, In browser globalThis is window and in node globalThis is global

var point = {
  x: null,
  y: null,

  init(x, y) {
    this.x = x;
    this.y = y;
  },
  toString() {
    return `(${this.x},${this.y})`;
  },
};

const init = point.init;
init(3, 4);
console.log("output: ", point.toString());
// output: (null,null)

console.log("x: ", x);
// x: 3
// Variable x and y is created ??!!

console.log("this: ", this);
//this: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
"use strict";

var point = {
  // ...
};

const init = point.init;
init(3, 4);
// TypeError: Cannot set properties of
// undefined (setting 'x')

console.log("this: ", this);
// this: undefined

This is proper way to do it

var point = {
  // ...
};

const init = point.init;
init.call(point, 3, 4);
// or: init.apply( point, [ 3, 4 ] )

point.x; // 3
point.y; // 4

So now you can borrow a function reference.

var point = {
  // ...
};

point.init(3, 4);

var anotherPoint = {};
point.init.call(anotherPoint, 5, 6);

// or you can do this

var similarPoint = {
  init: point.init,
  toString: point.toString,
};
similarPoint.init(7, 8);

point.x; // 3
point.y; // 4
anotherPoint.x; // 5
anotherPoint.y; // 6
similarPoint.x; // 7
similarPoint.y; // 8

There is another way to use function from antoher object. You can use keyword new.

var point = {
  init: function () {
    /* .. */
  },
};

var anotherPoint = new point.init(3, 4);

anotherPoint.x; // 3
anotherPoint.y; // 4

new keyword hijacks a function and forces its behavior into a different mode than a normal invocation. Here are the 4 special steps that JS performs when a function is invoked with new:

  1. create a brand new empty object, out of thin air.
  2. link the [[Prototype]] of that new empty object to the function's .prototype object
  3. invoke the function with the this context set to that new empty object.
  4. if the function doesn't return its own object value explicitly (with a return .. statement), assume the function call should instead return the new object

Step 4 implies that if you new invoke a function that does return its own object, like return { .. }, etc, then the new object from steps 1-3 is not returned.

// alternative to:
//   var anotherPoint = new point.init(3,4)

var anotherPoint;
// this is a bare block to hide local
// `let` declarations
{
  // (Step 1)
  let tmpObj = {};

  // (Step 2)
  Object.setPrototypeOf(tmpObj, point.init.prototype);
  // or: tmpObj.__proto__ = point.init.prototype

  // (Step 3)
  let res = point.init.call(tmpObj, 3, 4);

  // (Step 4)
  anotherPoint = typeof res !== "object" ? tmpObj : res;
}

There are four rules for this context assignment in function calls:

  1. Is the function invoked with new, creating and setting a new this?
  2. Is the function invoked with call(..) or apply(..), explicitly setting this?
  3. Is the function invoked with an object reference at the call-site (e.g., point.init(..)), implicitly setting this?
  4. If none of the above... are we in non-strict mode? If so, default the this to globalThis. But if in strict-mode, default the this to undefined.

These rules, in this order, are how JS determines the this for a function invocation. If multiple rules match a call-site (e.g., new point.init.call(..)), the first rule from the list to match wins.

Arrow functions

Arrow functions differ in their handling of this: they inherit this from the parent scope at the time they are defined. This behavior makes arrow functions particularly useful for callbacks and preserving context. However, arrow functions do not have their own this binding. Therefore, their this value cannot be set by bind(), apply() or call() methods, nor does it point to the current object in object methods.


If you wish to learn more about this subject, follow these links:

https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/objects-classes/ch4.md