Тут же нет ничего сложного
Любой объект содержит свойство __proto__, значением которого является ссылка на другой объект, который называется прототипом или null.
Любая функция - это тоже объект, который может содержать различные свойства. При создании (определении) функции у нее обязательно создается свойство prototype, значением которого является объект, содержащий свойство constructor, которое является ссылкой на саму функцию. Этот prototype мы потом можем дополнять (или вообще изменить).
Итак.
Когда мы написали
function fun () {}
создался
fun.prototype = {
constructor : fun
}
Теперь мы можем, например, что то добавлять в fun.prototype
fun.prototype.f1 = function () {...};
fun.prototype.f2 = function () {...};
Когда мы создаем новый объект с помощью new
let o = new fun()
происходит следующее:
Создается пустой объект
в его свойство __proto__ записывается fun.prototype
let o={};
o.__proto__ = fun.prototype;
Далее вызывается функция fun и этот объект передается ей в качестве this. Как при вызове
fun.apply(o)