Every object has an internal [[Prototype]] link. When a lookup misses on the object, the engine follows the link, then the next, until it reaches null. Every ordinary function has a prototype property, and new F(args) creates an object linked to F.prototype, runs F with this bound to it, and returns it unless F returns another object. Classes (Classes) are syntax over this model.

this is set by the call, not the definition: obj.m() binds obj; a bare m() gets undefined in strict mode (the global object in sloppy scripts); call(), apply() and bind() set it explicitly; arrow functions inherit the enclosing this, which makes them the fix for callbacks that lose it.
"use strict";
function Animal(name) { this.name = name; }
Animal.prototype.speak = function () { return `${this.name} speaks`; };
function Dog(name) { Animal.call(this, name); } // run the parent constructor
Object.setPrototypeOf(Dog.prototype, Animal.prototype);
Dog.prototype.bark = function () { return `${this.name} barks`; };
const rex = new Dog("Rex");
const { speak } = rex; // detached method
console.log(rex.bark(), rex.speak(), rex instanceof Animal, speak.call({ name: "Tom" }));
try { speak(); } catch (e) { console.log(`${e.name}: this is undefined`); }Output
Rex barks Rex speaks true Tom speaks TypeError: this is undefined