// Get prototype of objectconst obj = {};const proto = Object.getPrototypeOf(obj);console.log(proto === Object.prototype); // true// Set prototype (avoid in production - slow!)const animal = { type: 'animal' };const dog = { breed: 'Labrador' };Object.setPrototypeOf(dog, animal);console.log(dog.type); // 'animal' (inherited)// Create object with specific prototype (preferred)const cat = Object.create(animal);cat.breed = 'Persian';console.log(cat.type); // 'animal' (inherited)// Check if object is in prototype chainconsole.log(animal.isPrototypeOf(dog)); // trueconsole.log(animal.isPrototypeOf(cat)); // trueconsole.log(Object.prototype.isPrototypeOf(dog)); // true (all objects)// Create object with null prototype (no inheritance)const noProto = Object.create(null);console.log(noProto.toString); // undefined (no Object.prototype)console.log(Object.getPrototypeOf(noProto)); // null// Prototype chain visualizationfunction showChain(obj, label = 'obj') { console.log(`${label}:`, obj); const proto = Object.getPrototypeOf(obj); if (proto) { showChain(proto, `${label}.__proto__`); } else { console.log('End of chain: null'); }}const myObj = { x: 1 };showChain(myObj);// myObj: { x: 1 }// myObj.__proto__: Object.prototype// End of chain: null
Important: Property lookup is read-only up the chain. Writing to a property always
creates/modifies own property (unless setter exists in prototype). Use Object.getPrototypeOf() and
Object.create() instead of deprecated __proto__. Avoid
Object.setPrototypeOf() for performance reasons.
2. Constructor Functions and new Operator
Concept
Description
Behavior
Constructor Function
Regular function used with new to create instances
Conventionally capitalized; sets up instance properties
Best Practice: For inheritance, use ES6 classes (cleaner syntax). If using constructor
functions, always use Object.create(Parent.prototype) to set up prototype chain, not
new Parent() or direct assignment. Always restore constructor property. Use
Object.create(null) for dictionary objects to avoid prototype pollution.
4. Prototype Methods and Property Override
Concept
Description
Behavior
Method Inheritance
Child inherits methods from parent's prototype
Shared method executed with child instance as this
Method Override
Child defines method with same name as parent
Child method shadows parent method in prototype chain
super Pattern
Call parent method from overridden child method
Use Parent.prototype.method.call(this) or ES6 super
Property Shadowing
Own property hides prototype property with same name
Lookup stops at first match (own properties checked first)
Dynamic Dispatch
this binding determined at call time, not definition
Enables polymorphism - same method name, different behavior
Pattern
Implementation
Use Case
Simple Override
Define method on child with same name
Completely replace parent behavior
Extend Parent Method
Call parent method + add child logic
Augment parent behavior
Delegate to Parent
Forward some calls to parent method
Conditional behavior based on state
Mixin Pattern
Copy methods from multiple sources
Multiple inheritance simulation
Example: Method inheritance and override
// Parent with methodsconst animal = { speak() { return `${this.name} makes a sound`; }, eat() { return `${this.name} is eating`; }};// Child inherits and overridesconst dog = Object.create(animal);dog.name = 'Rex';// Inherited method (no override)console.log(dog.eat()); // "Rex is eating" (from animal)// Override speak methoddog.speak = function() { return `${this.name} barks loudly`;};console.log(dog.speak()); // "Rex barks loudly" (overridden)// Access parent method explicitlyconsole.log(animal.speak.call(dog)); // "Rex makes a sound" (parent method)// Another child with different overrideconst cat = Object.create(animal);cat.name = 'Whiskers';cat.speak = function() { return `${this.name} meows softly`;};console.log(cat.speak()); // "Whiskers meows softly" (different override)// Polymorphism in actionconst animals = [dog, cat];animals.forEach(a => console.log(a.speak()));// "Rex barks loudly"// "Whiskers meows softly"
Example: Calling parent methods (super pattern)
// Constructor-based inheritancefunction Animal(name) { this.name = name;}Animal.prototype.describe = function() { return `This is ${this.name}`;};Animal.prototype.speak = function() { return `${this.name} makes a sound`;};function Dog(name, breed) { Animal.call(this, name); // Call parent constructor this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;// Override with extension - call parent methodDog.prototype.describe = function() { // Call parent method const parentDesc = Animal.prototype.describe.call(this); // Add child-specific info return `${parentDesc}, a ${this.breed}`;};Dog.prototype.speak = function() { // Completely override (no parent call) return `${this.name} barks`;};const dog = new Dog('Rex', 'Labrador');console.log(dog.describe()); // "This is Rex, a Labrador" (extended)console.log(dog.speak()); // "Rex barks" (overridden)// ES6 class with super keyword (cleaner)class AnimalClass { constructor(name) { this.name = name; } describe() { return `This is ${this.name}`; } speak() { return `${this.name} makes a sound`; }}class DogClass extends AnimalClass { constructor(name, breed) { super(name); // Call parent constructor this.breed = breed; } describe() { // Call parent method with super return `${super.describe()}, a ${this.breed}`; } speak() { // Override without calling parent return `${this.name} barks`; }}const modernDog = new DogClass('Max', 'Beagle');console.log(modernDog.describe()); // "This is Max, a Beagle"console.log(modernDog.speak()); // "Max barks"
// Mixin: reusable method collectionsconst canEat = { eat(food) { return `${this.name} is eating ${food}`; }};const canWalk = { walk() { return `${this.name} is walking`; }};const canSwim = { swim() { return `${this.name} is swimming`; }};// Mixin helper functionfunction mixin(target, ...sources) { Object.assign(target, ...sources); return target;}// Create object with multiple mixinsfunction Animal(name) { this.name = name;}// Add mixins to prototypemixin(Animal.prototype, canEat, canWalk);const dog = new Animal('Rex');console.log(dog.eat('bone')); // "Rex is eating bone"console.log(dog.walk()); // "Rex is walking"// console.log(dog.swim()); // Error: swim not available// Different mix of capabilitiesfunction Fish(name) { this.name = name;}mixin(Fish.prototype, canEat, canSwim);const fish = new Fish('Nemo');console.log(fish.eat('algae')); // "Nemo is eating algae"console.log(fish.swim()); // "Nemo is swimming"// console.log(fish.walk()); // Error: walk not available// ES6 class with mixinsconst Flyable = { fly() { return `${this.name} is flying`; }};class Bird { constructor(name) { this.name = name; }}// Apply mixin to classObject.assign(Bird.prototype, canEat, Flyable);const bird = new Bird('Tweety');console.log(bird.eat('seeds')); // "Tweety is eating seeds"console.log(bird.fly()); // "Tweety is flying"
Best Practice: Use method override for polymorphism. Call parent methods with
super (ES6 classes) or Parent.prototype.method.call(this) (constructor functions). Use
mixins for horizontal code reuse. Remember: methods use this binding, so inherited methods access
instance properties correctly.
5. instanceof and Prototype Testing
Method/Operator
Syntax
Returns
What It Checks
instanceof
obj instanceof Constructor
boolean
Is Constructor.prototype in obj's prototype chain?
isPrototypeOf()
proto.isPrototypeOf(obj)
boolean
Is proto in obj's prototype chain?
Object.getPrototypeOf()
Object.getPrototypeOf(obj)
object or null
Returns obj's direct prototype
hasOwnProperty()
obj.hasOwnProperty(prop)
boolean
Is prop an own property (not inherited)?
Object.hasOwn() ES2022
Object.hasOwn(obj, prop)
boolean
Safer alternative to hasOwnProperty
in operator
'prop' in obj
boolean
Is prop in obj or its prototype chain?
Test
Own Property
Inherited Property
Nonexistent
obj.prop
value
value
undefined
'prop' in obj
true
true
false
obj.hasOwnProperty('prop')
true
false
false
Object.hasOwn(obj, 'prop')
true
false
false
Example: instanceof operator
// Constructor-based type checkingfunction Animal(name) { this.name = name;}function Dog(name, breed) { Animal.call(this, name); this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;const dog = new Dog('Rex', 'Labrador');// instanceof checks prototype chainconsole.log(dog instanceof Dog); // trueconsole.log(dog instanceof Animal); // trueconsole.log(dog instanceof Object); // true (all objects)// Arraysconst arr = [1, 2, 3];console.log(arr instanceof Array); // trueconsole.log(arr instanceof Object); // true// Primitivesconsole.log('hello' instanceof String); // false (primitive, not object)console.log(new String('hello') instanceof String); // true (wrapper object)console.log(42 instanceof Number); // false// instanceof can be fooledfunction Fake() {}Fake.prototype = dog; // Set prototype to instanceconst fake = new Fake();console.log(fake instanceof Dog); // true! (dog in prototype chain)// Object.createconst parent = { type: 'parent' };const child = Object.create(parent);// No constructor function, instanceof doesn't work well// console.log(child instanceof ???); // What to check?
Example: isPrototypeOf() method
const animal = { eats: true};const rabbit = Object.create(animal);rabbit.jumps = true;const longEaredRabbit = Object.create(rabbit);longEaredRabbit.earLength = 10;// Check prototype relationshipsconsole.log(animal.isPrototypeOf(rabbit)); // trueconsole.log(animal.isPrototypeOf(longEaredRabbit)); // true (anywhere in chain)console.log(rabbit.isPrototypeOf(longEaredRabbit)); // trueconsole.log(rabbit.isPrototypeOf(animal)); // false (wrong direction)// Works with any object, not just constructorsconsole.log(Object.prototype.isPrototypeOf(rabbit)); // true (all objects)console.log(Object.prototype.isPrototypeOf({})); // true// Compare with instanceof (requires constructor)function Animal() {}const dog = new Animal();console.log(dog instanceof Animal); // trueconsole.log(Animal.prototype.isPrototypeOf(dog)); // true (equivalent)// null prototypeconst noProto = Object.create(null);console.log(Object.prototype.isPrototypeOf(noProto)); // false
Example: Property ownership testing
const parent = { inherited: 'from parent', sharedMethod() { return 'shared'; }};const child = Object.create(parent);child.own = 'own property';// 'in' operator - checks own and inheritedconsole.log('own' in child); // true (own)console.log('inherited' in child); // true (inherited)console.log('sharedMethod' in child); // true (inherited)console.log('nonexistent' in child); // false// hasOwnProperty - checks only own propertiesconsole.log(child.hasOwnProperty('own')); // trueconsole.log(child.hasOwnProperty('inherited')); // false (inherited)console.log(child.hasOwnProperty('sharedMethod')); // false// Object.hasOwn (ES2022) - safer alternativeconsole.log(Object.hasOwn(child, 'own')); // trueconsole.log(Object.hasOwn(child, 'inherited')); // false// Why Object.hasOwn is saferconst obj = Object.create(null); // No prototypeobj.prop = 'value';// obj.hasOwnProperty('prop'); // Error: hasOwnProperty not availableconsole.log(Object.hasOwn(obj, 'prop')); // true (works!)// Distinguish own vs inherited in iterationfor (let key in child) { if (child.hasOwnProperty(key)) { console.log(`Own: ${key}`); } else { console.log(`Inherited: ${key}`); }}// Own: own// Inherited: inherited// Inherited: sharedMethod// Object.keys() only returns own enumerable propertiesconsole.log(Object.keys(child)); // ['own']// Get all properties including inherited (manual)function getAllProperties(obj) { const props = new Set(); let current = obj; while (current) { Object.getOwnPropertyNames(current).forEach(p => props.add(p)); current = Object.getPrototypeOf(current); } return Array.from(props);}console.log(getAllProperties(child));// Includes: own, inherited, sharedMethod, toString, hasOwnProperty, etc.