[[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))