В JS все является объектом.
function A () {
// пустой конструктор
}
var Unnamed = function () {
};
var b = new A(); // цепочка прототипов у a будет такой: A <- Function <- Object
var c = new Object; // синоним {}, прототипом будет Object.
var d = new Unnamed();
console.log(b.constructor.name); // "A"
console.log(d.constructor.name); // ""
console.log(b instanceof Object); // true
console.log({} instanceof Object); // true
Скажем у Object и Number это базовые типы и у них общий прототип. Просто {} или new Object - просто интанс Object-а, и именованные функции - новые типы, прототипом которого будет Object (так или иначе если спускаться вниз по цепочке прототипов).
Внутри все веселее. Вируатльной машине вообще нет дела что у нее, Object или его потомок Foo, доступ к каждому свойству или методу всеравно в рантайме вычисляется. Она только оптимизировать это дело может для своего внутреннего представления. У каждого объекта в JS есть хидден-класс. То есть:
var a = {x: 1}, b = {x: 2}, c = {y: 3};
в этом случае a и b будут иметь один и тот же хидден класс, а c - другой.
Когда мы пишем вот такой вот код:
function foo(bar) {
this.bar = bar;
if (bar === false) {
this.falthy = true;
}
}
var a = new foo(true), b = new foo(bar);
То типы этих двух переменных будут совпадать, а хидден классы нет. Таким образом во внутреннем представлении это будут два разных типа и оно не сможет применить одни и те же оптимизации. Так же если у нас был объект с одним свойством, а мы туда добавили еще десяток (цифра с потолка), то оптимизирующий компилятор перестает заниматься этими оптимизациями и будет думать что наш объект используется как простая хэш-мэпа.
То есть виртуальной машине как бы плевать на тип объекта, так как она понимает, что объекты меняются в рантайме и могут поменяться когда захотят. Она смотрит на тип только при сравнении. В целом же ей фиалетово, Object у нас или наш Foo, она будет смотреть только на хидден класс и исходить из того, что оно раньше делало с объектами с таким хидден классом.