Before of all let’s consider what an object is in JavaScript: The object is an instance of a specific reference type. Wait, but what is a reference type? In ECMAScript, reference types are structures used to group data and functionality together and are often incorrectly called classes. Reference types are also sometimes called object definitions because they describe the properties and methods that objects should have. Let’s remember how we create a custom object in JavaScript: const person = new Object(); person.name = 'Olga'; person.age = 26 person.sayName = function() { console.log(this.name) } This line of code declares the variable “person” and assigns it a value — a new instance of the Object reference type. To do this we used a constructor which creates a simple object with only the default properties and methods. After we added our custom properties and methods to it. Object() Also, there is a shorthand form of object definition which mostly in use now: const person = { name : 'Olga', age : 26, sayName: function() { console.log(this.name) } }; Object creation Using the Object constructor or an object literal is a convenient way to create single objects, but what about creating multiple objects with the same interface? This will entail a lot of code duplication. To solve this problem, developers began using a variation of the factory pattern. The Factory Pattern The factory pattern is a well-known design pattern used in software engineering to abstract away the process of creating specific objects. let's look at the example of implementing this pattern in JavaScript: function createAnimal(name, speed) { const obj = new Object(); obj.name = name; obj.speed = speed; obj.getSpeedInfo = function() { console.log(`I run ${this.speed}`); }; return obj; } const turtle = createAnimal('Bruno', 'slow'); const rabbit = createAnimal('Roger', 'fast'); This approach solved the problem of creating multiple similar objects. But the factory pattern didn’t address the issue of object identification (what type of object an object is). The Constructor Pattern Constructors in ECMAScript are used to create specific types of objects. There are native constructors, such as Object and Array, which are available automatically in the execution environment at runtime. Also, you can define custom constructors that have properties and methods for your type of object. The previous example can be rewritten using the constructor pattern next way: function Animal(name, speed) { this.name = name; this.speed = speed; this.getSpeedInfo = function(){ console.log(`I run ${this.speed}`); }; } const turtle = new Animal('Bruno', 'slow'); const rabbit = new Animal('Roger', 'fast'); console.log(turtle.getSpeedInfo === rabbit.getSpeedInfo); //false As you can see this approach is extremely suboptimal in terms of memory allocation and power, because with such an entity description, you will receive in each instance not a reference to the method, which is stored in the memory of the constructor function, but you will copy this function to each new instance. We can solve this problem as follows: getSpeedInfo() function Animal(name, speed) { this.name = name; this.speed = speed; this.getSpeedInfo = getSpeedInfo; } function getSpeedInfo() { console.log(`I run ${this.speed}`); } const turtle = new Animal('Bruno', 'slow'); const rabbit = new Animal('Roger', 'fast'); console.log(turtle.getSpeedInfo === rabbit.getSpeedInfo); //true As they get speed info property now contains just a pointer to a function, both turtle and rabbit end up sharing the a function that is defined in the global scope. This solves the problem of having duplicate functions but also creates some clutter in the global scope. Also with more methods, all of a sudden the custom reference type definition is no longer nicely grouped in the code. These problems are addressed by using the prototype pattern. getSpeedInfo() The Prototype Pattern Some functions are created with a prototype property, which is an object containing properties and methods that should be available to instances of a particular reference type. This object is a prototype for the object to be created once the constructor is called. The benefit of using the prototype is that all of its properties and methods are among object instances and instance. Instead of assigning object information in the constructor, they can be assigned directly to the prototype: shared not created for each function Animal() {} Animal.prototype = { name: 'Bruno', speed: 'fast', getSpeedInfo: function () { console.log(`I run ${this.speed}`); } } const rabbit = new Animal(); const turtle = new Animal(); console.log(rabbit.getSpeedInfo === turtle.getSpeedInfo); //true How Prototypes Work When a function is created, its prototype property is also created. By default, all prototypes automatically get a property called constructor that points back to the function on which it is a property. In the previous example, the Animal.prototype.constructor points to Animal. Then other properties and methods may be added to the prototype: When defining a custom constructor, the prototype gets the constructor property only by default (all other methods are inherited from the Object). Each time the constructor is called to create a new instance, that instance has an internal pointer to the constructor’s prototype — . The important thing to understand is that a direct link exists between the instance and the constructor’s prototype but not between the instance and the constructor. [[Prototype]] function Animal() {} Animal.prototype.name = 'Bruno'; Animal.prototype.speed = 'fast'; Animal.prototype.getSpeedInfo = function(){ console.log(`I run ${this.speed}`); }; const turtle = new Animal(); const rabbit = new Animal(); console.log(turtle.hasOwnProperty('speed')); //false turtle.speed = 'slow'; console.log(turtle.speed); //'slow' - from instance console.log(turtle.hasOwnProperty('speed')); //true console.log(rabbit.speed); //'fast' - from prototype console.log(rabbit.hasOwnProperty('speed')); //false delete turtle.speed; console.log(turtle.speed); //'fast' - from the prototype console.log(turtle.hasOwnProperty('speed')); //false The parent of the child is called the prototype, which is where the name "prototype inheritance" comes from. Thus, there is a very economical consumption of memory: But not every function has . Only constructor functions have it. [[Prototype]] Let’s remember what is constructor functions. The only difference between constructor functions and other functions is how they are called. Any function that is called with the new operator acts as a constructor, whereas any function called without it acts just as you would expect a normal function call to act. Arrow functions, functions defined by method syntax, asynchronous functions, built-in functions, and others do not have . them proto In 2012 appeared in the standard. Thanks to this, we were able to create objects with a given prototype but did have the ability to get or set it wherever we need. Some browsers implemented the non-standard accessor that allowed developers to get/set a prototype at any time. Object.create __proto__ The is not a property of an object, but an accessor property of . In other words, it is a way to access , it is not itself. __proto__ Object.prototype [[Prototype]] [[Prototype]] If it is used as a getter, returns the object's : [[Prototype]] function Person (name) { this.name = name; } Person.prototype.greeting = function () { console.log('Hello'); }; let me = new Person('Olya'); console.log(me.__proto__ === Person.prototype); // true We can write the prototype chain from the “ “ image next way: 3. Prototype chain const arr = new Array(); console.log(arr.__proto__ === Array.prototype) //true console.log(arr.__proto__.__proto__ === Object.prototype) //true If you use as a setter, returns : __proto__ undefined function Person (name) { this.name = name; } Person.prototype.greeting = function () { console.log('Hello'); }; let me = new Person('Olya'); const MyOwnPrototype = { greeting() { console.log('Hey hey!'); }, }; me.__proto__ = MyOwnPrototype console.log(me.__proto__ === Person.prototype); // false Not every object in JavaScript has this accessor. Objects that do not inherit from (for example, objects created as ) don’t have it. Object.prototype Object.create(null) lets you set the prototype of an existing object, but generally, that's not a good idea. Let’s see what the says: __proto__ documentation Changing the of an object is, by the nature of how modern JavaScript engines optimise property accesses, currently a very slow operation in every browser and JavaScript engine. [[Prototype]] is supported today in most browsers, but it is a legacy feature to ensure compatibility with web browsers. For better support, preferably use and instead. Object.prototype.__proto__ Object.getPrototypeOf() Object.setPrototypeOf() In 2022, it was officially allowed to use in object literals , but not as a getter/setter . __proto__ {...} obj.__proto__ It means that the only usage of is as a property when creating a new object — . But there’s a special method for this as well — __proto__ { __proto__: ... } — creates an empty object with given as and optional property descriptors: Object.create(proto, [descriptors]) proto [[Prototype]] const animal = { speed: 'fast' }; // create a new object with animal as a prototype const rabbit = Object.create({__proto__: animal}); const turtle = Object.create(animal); // same as {__proto__: animal} So why we shouldn't use but should use if performance is the same? __proto__ Object.setPrototypeOf itself is discouraged because it prevents an arbitrary object to be safely used as a dictionary: __proto__ let obj = {}; let key = '__proto__'; obj[key] = 'some value'; console.log(obj[key]); // [object Object], not 'some value'! The property is special: it must be either an object or . A string can not become a prototype. That’s why an assignment of a string to is ignored. So it is a bug. __proto__ null __proto__ Object.getPrototypeOf(), Object.setPrototypeOf() In 2015, and were added to the standard, to perform the same functionality as and now they are recommended methods to get/set a prototype. Object.setPrototypeOf Object.getPrototypeOf __proto__ – returns the of . Object.getPrototypeOf(obj) [[Prototype]] obj – sets the of to . Object.setPrototypeOf(obj, proto) [[Prototype]] obj proto is generally considered the proper way to set the prototype of an object. You should always use it in favor of the deprecated accessor. Object.setPrototypeOf() Object.prototype.__proto__ Remember, it is not advisable to use instead of due to performance and readability reasons. setPrototypeOf() extends Let’s use our new methods in practice: function Person (name) { this.name = name; } Person.prototype.greeting = function () { console.log('Hello'); }; let me = new Person('Olya'); const MyOwnPrototype = { greeting() { console.log('Hey hey!'); }, }; Object.setPrototypeOf(me, MyOwnPrototype); console.log(Object.getPrototypeOf(me) === Person.prototype); //false console.log(Object.getPrototypeOf(me) === MyOwnPrototype); // true Conclusion You shouldn’t change existing objects. This is a bad practice both from the architecture side and from the speed side. [[Prototype]] Of course, we can get/set at any time but before it you should ask yourself: “Is it really necessary in this case or should I reconsider my architecture to not get into this situation next time?“. A good practice is to set once at the object creation time and don’t modify it anymore: inherits from , and it is what it is. [[Prototype]] [[Prototype]] rabbit animal JavaScript engines support this ideology as much as possible. Subsequent prototype changing with or breaks internal optimizations for object property access operations and it will be very costly in terms of performance. So be careful with using those features unless you know what you’re doing. Object.setPrototypeOf obj.__proto__