[[Prototype]] objects have a hidden property [[Prototype]] which references to another object and contains its properties and methods when read a none existing property or call method, JS tries to find it in the [[Prototype]] - prototypal inheritance [[Prototype]] is hidden, but can be set via special name __proto__ this is always the object from where the method is called let obj1 = { eats: true, name: "John", surname: "Smith", hi() { alert("Hi, I am John") }, set fullName(value) { [this.name, this.surname] = value.split(" "); }, get fullName() { return `${this.name} ${this.surname}`; } } let obj2 = { jumps: true } // your object obj2.__proto__ = obj1 // or obj2 = { jumps: true, __proto__: obj1 } obj2.eats // true obj2.hi() // Hi, I am John obj2.fullName // John Smith obj2.fullName = "Alice Cooper" obj2.fullName // Alice Cooper __proto__ __proto__ is a getter/setter for [[Prototype]] It resides in Object.prototype , just like other methods. __proto__ is not the same as [[Prototype]] __proto__ value can be either an object or null , other types are ignored. There can be only one [[Prototype]] per object. An object may not inherit from two others. It is outdated & exists for historical reasons. References can’t go in circles. JavaScript will throw an error. Instead of __proto__ recommended to use following methods: Object.create(proto, [descriptors]) - creates an empty object with given proto as [[Prototype]] and optional property descriptors. Object.getPrototypeOf(obj2) Object.setPrototypeOf(obj2, obj1) Prototype chain let animal = { eats: true, walk() { alert("Animal walk") } } let rabbit = { jumps: true, __proto__: animal } let longEar = { earLength: 10, __proto__: rabbit } // or Object.setPrototypeOf(rabbit, animal) Object.setPrototypeOf(longEar, rabbit) longEar.walk() // Animal walk longEar.jumps // true (from rabbit) for…in loop Iterates over inherited properties also let animal = { eats: true } let rabbit = { jumps: true, __proto__: animal } Object.keys(rabbit) // jumps for (let prop in rabbit) alert(prop) // jumps, eats obj.hasOwnProperty(key) Returns true if an object has its own (not inherited) property named key let animal = { eats: true } let rabbit = { jumps: true, __proto__: animal } for(let prop in rabbit) { let isOwn = rabbit.hasOwnProperty(prop) if (isOwn) { alert(`Our: ${prop}`); // Our: jumps } else { alert(`Inherited: ${prop}`); // Inherited: eats } } Constructor function & .prototype Constructor function has a prototype property, which assigns [[Prototype]] to an object .prototype property is used when constructor function is called The default .prototype is an object with the only property constructor, which points back to the function itself we may point constructor function's .prototype to an object to let constructed future object inherit from that object function Rabbit() {} Rabbit.prototype // { constructor: Rabbit } let rabbit = new Rabbit(); // inherits from {constructor: Rabbit} rabbit.constructor == Rabbit // true (from prototype) let animal = { eats: true } function Rabbit(name) { this.name = name } Rabbit.prototype = animal let rabbit = new Rabbit("White Rabbit") // rabbit.__proto__ == animal rabbit.eats // true May also add some properties to .prototype function Rabbit() {} Rabbit.prototype = { jumps: true, constructor: Rabbit }; let rabbit = new Rabbit(); rabbit.jumps // true Native prototypes All built-in prototypes have Object.prototype on the top Strings, numbers and booleans are not objects, but wrapper object silently created with built-in constructors String , Number and Boolean null & undefined do not have object wrappers const arr = [] arr.__proto__ === Array.prototype // true arr.__proto__.__proto__ === Object.prototype // true arr.__proto__.__proto__.__proto__ // null Change native prototypes Native prototypes can be modified It becomes available to all variables BAD practice!!! String.prototype.show = function() { alert(this) }; "BOOM!".show(); // BOOM! String.prototype.repeat = function(n) { return new Array(n + 1).join(this) }; "La".repeat(3) // 'LaLaLa' Function.prototype.defer = function(ms) { setTimeout(this, ms) }; function f() { alert("Hello!") } f.defer(1000); // "Hello!" in 1 sec Borrow from prototypes // array-like object let obj = { 0: "Hello", 1: "world!", length: 2, } // borrow method obj.join = Array.prototype.join obj.join(',') // Hello, world! Prototype methods Object.create(protoObj, [propertiesObject]) let animal = { eats: true } let rabbit = Object.create(animal, { jumps: { value: true } }) rabbit.jumps // true rabbit.eats // true // "very plain" object w/o a prototype let obj = Object.create(null) Object.getPrototypeOf(obj) Object.getPrototypeOf(rabbit) === animal // true Object true cloning Including all properties: enumerable and non-enumerable, data properties and setters/getters let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))