Four names, two ideas: the link ([[Prototype]], with getPrototypeOf and __proto__ as ways to reach it) and the constructor property that feeds it (prototype).
[[Prototype]]is an internal slot on every object: the link the engine follows during property lookup. Code can't touch the slot directly.Object.getPrototypeOf(obj)andObject.setPrototypeOf(obj, proto)(alsoReflect.*) read and write that slot. They are the standard API. PreferObject.create(proto)to choose a prototype at creation.obj.__proto__is a legacy accessor property defined onObject.prototype. Its getter returns[[GetPrototypeOf]]ofthis; its setter calls[[SetPrototypeOf]]. It exists for web compatibility and is marked legacy in the spec. Don't use it in new code. Because it is inherited, objects with noObject.prototypein their chain (Object.create(null)) don't have it.F.prototypeis an ordinary data property of constructor functions (not arrow functions or methods). Its value is the object thatnew F()uses as the[[Prototype]]of the new instance. It is notF's own prototype:Object.getPrototypeOf(F)isFunction.prototype.
So for const ann = new User():
Object.getPrototypeOf(ann) === User.prototype
User.prototype.constructor === User
Object.getPrototypeOf(User) === Function.prototype
Each ordinary function gets a fresh prototype object whose only property is a non-enumerable constructor pointing back at the function. constructor is inherited, not an own property of instances, and it is just a convention: replacing F.prototype with a new object loses it.
An object literal can set the prototype with { __proto__: proto }. That literal form is different from the accessor: it is specified syntax for non-computed __proto__ keys, and it doesn't trigger the setter.
Changing [[Prototype]] after creation is slow in every engine (MDN says so), so set it at creation.