|

JavaScript 29 🧬 Prototype

A prototype is a mechanism that allows you to add properties and methods to objects. Every JavaScript function has a prototype property, which refers to an object known as the prototype object — a template from which all instances of the function’s objects are created.


A Quick Look at the Examples

// 1. Constructor function
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 2. Adding method to prototype
Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// 3. Creating instances
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);

person1.greet(); // "Hello, my name is Alice and I am 30 years old."
person2.greet(); // "Hello, my name is Bob and I am 25 years old."

// 4. Accessing the prototype
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
console.log(person1.__proto__ === Person.prototype);              // true

// 5. Extending built-in prototypes
Array.prototype.sum = function() {
    return this.reduce((acc, num) => acc + num, 0);
};

const numbers = [1, 2, 3, 4];
console.log(numbers.sum()); // 10

a. What is the Prototype?

A prototype is a mechanism that allows you to add properties and methods to objects. Every JavaScript function has a prototype property that refers to an object known as the prototype object. This prototype object serves as a template from which all instances of the function’s objects are created.

How Prototypes Work

StepDescription
1. Function DeclarationWhen you declare a function, it automatically gets a prototype property
2. Creating ObjectsWhen you create an object with new, it inherits properties/methods from the constructor’s prototype
3. InheritanceThe new object has a hidden [[Prototype]] property that points to the constructor’s prototype object

Visual:

┌─────────────────────────────┐
│  Person (function)          │
│  ├── prototype ────────┐    │
│  └── ...               │    │
└────────────────────────┼────┘
                         │
                         ▼
              ┌──────────────────────┐
              │  Person.prototype    │
              │  ├── greet: function │
              │  └── constructor ────┼──→ Person
              └──────────┬───────────┘
                         │
                         │ [[Prototype]]
              ┌──────────┴───────────┐
              │                      │
              ▼                      ▼
      ┌──────────────┐       ┌──────────────┐
      │  person1     │       │  person2     │
      │  ├── name    │       │  ├── name    │
      │  └── age     │       │  └── age     │
      └──────────────┘       └──────────────┘

b. Accessing and Modifying Prototypes

Prototype Chain

When you access a property or method on an object, JavaScript:

  1. First looks for it on the object itself
  2. If not found, looks up the prototype chain
  3. Continues until it finds the property/method or reaches the end (i.e., null)
const person1 = new Person('Alice', 30);

// Looking up "name" — found on person1
console.log(person1.name); // "Alice"

// Looking up "greet" — NOT on person1, found on Person.prototype
person1.greet(); // Works!

// Looking up "toString" — NOT on person1, NOT on Person.prototype,
// found on Object.prototype (further up the chain)
console.log(person1.toString()); // "[object Object]"

// Looking up "nonexistent" — reaches null, returns undefined
console.log(person1.nonexistent); // undefined

Visual chain:

person1
  │
  ├── name: "Alice"
  ├── age: 30
  │
  └── [[Prototype]] ──→ Person.prototype
                            │
                            ├── greet: function
                            ├── constructor: Person
                            │
                            └── [[Prototype]] ──→ Object.prototype
                                                      │
                                                      ├── toString: function
                                                      ├── hasOwnProperty: function
                                                      └── [[Prototype]] ──→ null

Efficiency

By defining methods on the prototype, you save memory — the method is shared across all instances rather than being duplicated for each instance.

// ❌ BAD: Each instance gets its own copy of greet
function PersonBad(name) {
    this.name = name;
    this.greet = function() { console.log("Hi " + this.name); };
}
// Every new PersonBad() creates a NEW function for greet — memory waste!

// ✅ GOOD: All instances share the same greet method
function PersonGood(name) {
    this.name = name;
}
PersonGood.prototype.greet = function() { console.log("Hi " + this.name); };
// Only ONE greet function exists — shared by all instances

Inheritance

Prototypes allow for inheritance — objects can inherit properties and methods from other objects.

function Animal(name) {
    this.name = name;
}

Animal.prototype.speak = function() {
    console.log(`${this.name} makes a sound.`);
};

function Dog(name, breed) {
    Animal.call(this, name); // Call parent constructor
    this.breed = breed;
}

// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// Add method specific to Dog
Dog.prototype.bark = function() {
    console.log(`${this.name} barks!`);
};

const dog = new Dog('Rex', 'Labrador');
dog.speak(); // "Rex makes a sound." (inherited from Animal)
dog.bark();  // "Rex barks!" (own method)

Accessing the Prototype

Two ways to access an object’s prototype:

MethodDescriptionStatus
Object.getPrototypeOf(obj)Standard method✅ Recommended
obj.__proto__Legacy accessor⚠️ Deprecated
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
console.log(person1.__proto__ === Person.prototype);              // true

// Also:
console.log(Person.prototype.isPrototypeOf(person1)); // true
console.log(person1 instanceof Person);              // true

Modifying the Prototype

You can add, modify, or delete properties and methods on a prototype object at any time.

function Person(name) {
    this.name = name;
}

const person = new Person('Alice');

// Add method AFTER instance creation — still works!
Person.prototype.greet = function() {
    console.log(`Hello, ${this.name}`);
};

person.greet(); // "Hello, Alice" — finds it via prototype chain

// Modify method
Person.prototype.greet = function() {
    console.log(`Hey there, ${this.name}!`);
};
person.greet(); // "Hey there, Alice!"

// Delete method
delete Person.prototype.greet;
// person.greet(); // ❌ TypeError: person.greet is not a function

⚠️ Caution: Modifying built-in objects’ prototypes (like Array, String, Object) can lead to unexpected behavior and is generally discouraged in production code.

// ⚠️ DANGEROUS: Modifying built-in prototypes
Array.prototype.sum = function() {
    return this.reduce((acc, num) => acc + num, 0);
};

const numbers = [1, 2, 3, 4];
console.log(numbers.sum()); // 10

// BUT: This can break other code!
for (let key in numbers) {
    console.log(key); // "0", "1", "2", "3", "sum" ← "sum" shows up!
}

Why it’s risky:

  • Other libraries may use the same name
  • for...in loops will iterate over your added methods
  • Future JavaScript versions may add conflicting methods

c. Prototype Examples

Example 1: Constructor Function

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Creates a constructor that sets name and age on each instance.


Example 2: Adding a Method to the Prototype

Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

Adds a greet method shared by all instances.


Example 3: Creating Objects Using the Constructor

const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);

Each instance has its own name and age, but shares greet.


Example 4: Using the Greet Method

person1.greet(); // "Hello, my name is Alice and I am 30 years old."
person2.greet(); // "Hello, my name is Bob and I am 25 years old."

JavaScript finds greet on Person.prototype.


Example 5: Accessing the Prototype

console.log(person1.__proto__ === Person.prototype);              // true
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true

Both access the same prototype object.


Example 6: Adding a Method to the Array Prototype

Array.prototype.sum = function() {
    return this.reduce((acc, num) => acc + num, 0);
};

const numbers = [1, 2, 3, 4];
console.log(numbers.sum()); // 10

Extends all arrays with a sum method — ⚠️ use with caution!


Complete Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Prototype</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #f8f9fa;
            color: #333;
            line-height: 1.6;
        }
        h1 { color: #007bff; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
        h2 { color: #28a745; border-left: 4px solid #28a745; padding-left: 15px; margin-top: 30px; }
        .demo-box {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            margin: 15px 0;
        }
        pre {
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 15px;
            border-radius: 8px;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
            font-size: 0.9rem;
            line-height: 1.8;
        }
        .keyword { color: #569cd6; }
        .string { color: #ce9178; }
        .number { color: #b5cea8; }
        .function { color: #dcdcaa; }
        .comment { color: #6a9955; }
        .boolean { color: #569cd6; }
        #output {
            background: #e9ecef;
            padding: 15px;
            border-radius: 8px;
            margin-top: 15px;
            min-height: 40px;
            font-family: 'Courier New', monospace;
            font-size: 0.85rem;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        th, td {
            padding: 10px;
            border: 1px solid #ddd;
            text-align: left;
        }
        th { background: #007bff; color: white; }
        tr:nth-child(even) { background: #f8f9fa; }
        .btn {
            padding: 10px 20px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            margin: 5px;
            transition: all 0.3s;
        }
        .btn:hover {
            background: #0056b3;
            transform: translateY(-2px);
        }
        .btn-success { background: #28a745; }
        .btn-success:hover { background: #1e7e34; }
        .chain-viz {
            font-family: 'Courier New', monospace;
            font-size: 0.85rem;
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 20px;
            border-radius: 8px;
            margin: 15px 0;
            white-space: pre;
            overflow-x: auto;
            line-height: 1.8;
        }
        .proto-key { color: #569cd6; }
        .proto-value { color: #ce9178; }
        .proto-method { color: #dcdcaa; }
        .proto-highlight { color: #4ec9b0; font-weight: bold; }
    </style>
</head>
<body>

    <h1>Prototype</h1>

    <div class="demo-box">
        <h2>1. Constructor Function and Prototype</h2>
        <pre>
<span class="keyword">function</span> <span class="function">Person</span>(name, age) {
    <span class="keyword">this</span>.name = name;
    <span class="keyword">this</span>.age = age;
}

<span class="function">Person</span>.prototype.greet = <span class="keyword">function</span>() {
    console.log(<span class="string">`Hello, my name is ${this.name} and I am ${this.age} years old.`</span>);
};

<span class="keyword">const</span> person1 = <span class="keyword">new</span> <span class="function">Person</span>(<span class="string">'Alice'</span>, <span class="number">30</span>);
<span class="keyword">const</span> person2 = <span class="keyword">new</span> <span class="function">Person</span>(<span class="string">'Bob'</span>, <span class="number">25</span>);

person1.<span class="function">greet</span>();
person2.<span class="function">greet</span>();
        </pre>
    </div>

    <div class="demo-box">
        <h2>2. Prototype Chain Visualization</h2>
        <div class="chain-viz">
<span class="proto-highlight">person1</span>
  ├── name: <span class="proto-value">"Alice"</span>
  ├── age: <span class="proto-value">30</span>
  │
  └── [[Prototype]] ──→ <span class="proto-highlight">Person.prototype</span>
                            ├── greet: <span class="proto-method">function</span>
                            ├── constructor: <span class="proto-method">Person</span>
                            │
                            └── [[Prototype]] ──→ <span class="proto-highlight">Object.prototype</span>
                                                      ├── toString: <span class="proto-method">function</span>
                                                      ├── hasOwnProperty: <span class="proto-method">function</span>
                                                      ├── valueOf: <span class="proto-method">function</span>
                                                      │
                                                      └── [[Prototype]] ──→ <span class="proto-value">null</span>
        </div>
    </div>

    <div class="demo-box">
        <h2>3. Memory Efficiency — Shared Methods</h2>
        <pre>
<span class="comment">// ❌ BAD: Each instance gets its own copy</span>
<span class="keyword">function</span> <span class="function">PersonBad</span>(name) {
    <span class="keyword">this</span>.name = name;
    <span class="keyword">this</span>.greet = <span class="keyword">function</span>() { console.log(<span class="string">"Hi "</span> + <span class="keyword">this</span>.name); };
}
<span class="comment">// Every new PersonBad() creates a NEW function</span>

<span class="comment">// ✅ GOOD: All instances share the method</span>
<span class="keyword">function</span> <span class="function">PersonGood</span>(name) {
    <span class="keyword">this</span>.name = name;
}
<span class="function">PersonGood</span>.prototype.greet = <span class="keyword">function</span>() { console.log(<span class="string">"Hi "</span> + <span class="keyword">this</span>.name); };
<span class="comment">// Only ONE greet function exists — shared by all instances</span>
        </pre>
    </div>

    <div class="demo-box">
        <h2>4. Prototype Methods Comparison</h2>
        <table>
            <tr>
                <th>Method</th>
                <th>Description</th>
                <th>Recommended?</th>
            </tr>
            <tr>
                <td><code>Object.getPrototypeOf(obj)</code></td>
                <td>Standard way to get prototype</td>
                <td>✅ Yes</td>
            </tr>
            <tr>
                <td><code>obj.__proto__</code></td>
                <td>Legacy accessor</td>
                <td>⚠️ Deprecated</td>
            </tr>
            <tr>
                <td><code>Person.prototype.isPrototypeOf(obj)</code></td>
                <td>Check if prototype is in chain</td>
                <td>✅ Yes</td>
            </tr>
            <tr>
                <td><code>obj instanceof Person</code></td>
                <td>Check if object is instance</td>
                <td>✅ Yes</td>
            </tr>
            <tr>
                <td><code>obj.hasOwnProperty(key)</code></td>
                <td>Check if property is on object itself</td>
                <td>✅ Yes</td>
            </tr>
        </table>
    </div>

    <div class="demo-box">
        <h2>5. Interactive: Prototype Explorer</h2>
        <div style="margin: 10px 0;">
            <button class="btn" onclick="showPersonProto()">Show person1.prototype</button>
            <button class="btn" onclick="showOwnProps()">Show own properties</button>
            <button class="btn btn-success" onclick="showProtoChain()">Show full chain</button>
            <button class="btn" onclick="addProtoMethod()">Add method to prototype</button>
            <button class="btn" onclick="checkProtoEquality()">Check prototype equality</button>
        </div>
        <div id="protoOutput" style="background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 8px; font-family: 'Courier New', monospace; font-size: 0.85rem; min-height: 100px; white-space: pre-wrap; margin-top: 10px;"></div>
    </div>

    <div class="demo-box">
        <h2>6. Live Output — All Examples</h2>
        <div id="output">Loading...</div>
    </div>

    <script>
        // ============================================
        // Prototype — Live Demo
        // ============================================

        let results = [];

        // 1. Constructor + prototype method
        results.push('📌 Constructor Function + Prototype Method:\n');

        function Person(name, age) {
            this.name = name;
            this.age = age;
        }

        Person.prototype.greet = function() {
            return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
        };

        const person1 = new Person('Alice', 30);
        const person2 = new Person('Bob', 25);

        results.push('  person1.greet() → ' + person1.greet());
        results.push('  person2.greet() → ' + person2.greet());
        results.push('');

        // 2. Prototype equality
        results.push('📌 Prototype Equality:\n');
        results.push('  Object.getPrototypeOf(person1) === Person.prototype → ' +
            (Object.getPrototypeOf(person1) === Person.prototype));
        results.push('  person1.__proto__ === Person.prototype → ' +
            (person1.__proto__ === Person.prototype));
        results.push('  Person.prototype.isPrototypeOf(person1) → ' +
            Person.prototype.isPrototypeOf(person1));
        results.push('  person1 instanceof Person → ' + (person1 instanceof Person));
        results.push('');

        // 3. Own vs inherited properties
        results.push('📌 Own vs Inherited Properties:\n');
        results.push('  person1.hasOwnProperty("name") → ' + person1.hasOwnProperty('name'));
        results.push('  person1.hasOwnProperty("age") → ' + person1.hasOwnProperty('age'));
        results.push('  person1.hasOwnProperty("greet") → ' + person1.hasOwnProperty('greet'));
        results.push('  → "greet" is inherited, not own!');
        results.push('');

        // 4. Prototype chain
        results.push('📌 Prototype Chain:\n');

        function getProtoChain(obj) {
            const chain = [];
            let current = obj;
            while (current !== null) {
                const name = current.constructor?.name || 'Object';
                chain.push(name + '.prototype');
                current = Object.getPrototypeOf(current);
            }
            chain.push('null');
            return chain;
        }

        results.push('  person1 chain: ' + getProtoChain(person1).join(' → '));
        results.push('');

        // 5. Shared methods (memory efficiency)
        results.push('📌 Shared Methods (Memory Efficiency):\n');

        function PersonBad(name) {
            this.name = name;
            this.greet = function() { return "Hi " + this.name; };
        }

        function PersonGood(name) {
            this.name = name;
        }
        PersonGood.prototype.greet = function() { return "Hi " + this.name; };

        const bad1 = new PersonBad('A');
        const bad2 = new PersonBad('B');
        const good1 = new PersonGood('A');
        const good2 = new PersonGood('B');

        results.push('  BAD: bad1.greet === bad2.greet → ' + (bad1.greet === bad2.greet));
        results.push('  GOOD: good1.greet === good2.greet → ' + (good1.greet === good2.greet));
        results.push('  → Prototype methods are SHARED!');
        results.push('');

        // 6. Modifying prototype after instance creation
        results.push('📌 Modifying Prototype After Instance Creation:\n');

        const person3 = new Person('Charlie', 40);
        results.push('  Before: person3.hasOwnProperty("sayBye") → ' + person3.hasOwnProperty('sayBye'));

        Person.prototype.sayBye = function() {
            return `Goodbye from ${this.name}`;
        };

        results.push('  After adding sayBye to prototype:');
        results.push('  person3.sayBye() → ' + person3.sayBye());
        results.push('  → Instances automatically get new methods!');
        results.push('');

        // 7. Inheritance example
        results.push('📌 Prototype Inheritance:\n');

        function Animal(name) {
            this.name = name;
        }

        Animal.prototype.speak = function() {
            return `${this.name} makes a sound.`;
        };

        function Dog(name, breed) {
            Animal.call(this, name);
            this.breed = breed;
        }

        Dog.prototype = Object.create(Animal.prototype);
        Dog.prototype.constructor = Dog;

        Dog.prototype.bark = function() {
            return `${this.name} barks!`;
        };

        const dog = new Dog('Rex', 'Labrador');
        results.push('  dog.speak() → ' + dog.speak() + ' (inherited)');
        results.push('  dog.bark() → ' + dog.bark() + ' (own)');
        results.push('  dog.breed → ' + dog.breed);
        results.push('  dog instanceof Dog → ' + (dog instanceof Dog));
        results.push('  dog instanceof Animal → ' + (dog instanceof Animal));
        results.push('  → Dog inherits from Animal!');
        results.push('');

        // 8. Extending Array prototype (with caution note)
        results.push('📌 Extending Array Prototype (⚠️ caution):\n');

        if (!Array.prototype.sum) {
            Array.prototype.sum = function() {
                return this.reduce((acc, num) => acc + num, 0);
            };
        }

        const numbers = [1, 2, 3, 4];
        results.push('  [1, 2, 3, 4].sum() → ' + numbers.sum());
        results.push('  ⚠️ Warning: Modifying built-in prototypes can cause issues!');
        results.push('  → for...in loops would iterate "sum"');
        results.push('  → Other libraries may use the same name');
        results.push('  → Better: create a custom utility function');
        results.push('');

        // 9. Class syntax (modern equivalent)
        results.push('📌 ES6 Class (Modern Equivalent):\n');

        class PersonClass {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }

            greet() {
                return `Hello, I'm ${this.name}`;
            }
        }

        const classPerson = new PersonClass('Dave', 35);
        results.push('  classPerson.greet() → ' + classPerson.greet());
        results.push('  → Classes are syntactic sugar over prototypes!');
        results.push('  typeof PersonClass → ' + typeof PersonClass);
        results.push('  classPerson.hasOwnProperty("greet") → ' + classPerson.hasOwnProperty('greet'));
        results.push('  → greet is still on the prototype!');
        results.push('');

        // 10. Practical: Property lookup
        results.push('📌 Practical: Property Lookup Order:\n');

        const lookupObj = new Person('Eve', 28);
        lookupObj.city = 'NYC'; // Own property

        results.push('  lookupObj.name → ' + lookupObj.name + ' (own)');
        results.push('  lookupObj.city → ' + lookupObj.city + ' (own)');
        results.push('  lookupObj.greet → ' + typeof lookupObj.greet + ' (inherited)');
        results.push('  lookupObj.toString → ' + typeof lookupObj.toString + ' (from Object.prototype)');
        results.push('  lookupObj.unknown → ' + lookupObj.unknown + ' (not found)');

        document.getElementById('output').textContent = results.join('\n');

        // ============================================
        // Interactive Prototype Explorer
        // ============================================

        function showPersonProto() {
            const output = document.getElementById('protoOutput');
            const proto = Object.getPrototypeOf(person1);

            let html = '<span style="color: #569cd6;">Object.getPrototypeOf(person1):</span>\n';
            html += '{\n';
            for (let key of Object.getOwnPropertyNames(proto)) {
                if (key === 'constructor') {
                    html += `  <span style="color: #dcdcaa;">${key}</span>: <span style="color: #569cd6;">[Function: Person]</span>,\n`;
                } else {
                    html += `  <span style="color: #dcdcaa;">${key}</span>: <span style="color: #569cd6;">[Function]</span>,\n`;
                }
            }
            html += '}';
            output.innerHTML = html;
        }

        function showOwnProps() {
            const output = document.getElementById('protoOutput');
            const ownProps = Object.keys(person1);

            let html = '<span style="color: #569cd6;">Object.keys(person1):</span>\n';
            html += '[\n';
            ownProps.forEach((key, i) => {
                const comma = i < ownProps.length - 1 ? ',' : '';
                html += `  <span style="color: #ce9178;">"${key}"</span>: <span style="color: #b5cea8;">"${person1[key]}"</span>${comma}\n`;
            });
            html += ']\n';
            html += '\n<span style="color: #4ec9b0;">→ Only own properties, no prototype methods!</span>';
            output.innerHTML = html;
        }

        function showProtoChain() {
            const output = document.getElementById('protoOutput');
            let current = person1;
            let html = '<span style="color: #569cd6;">Prototype Chain:</span>\n\n';
            let depth = 0;

            while (current !== null) {
                const indent = '  '.repeat(depth);
                const name = current.constructor?.name || 'Object';
                const props = Object.getOwnPropertyNames(current).filter(p => p !== 'constructor');

                html += `${indent}<span style="color: #4ec9b0;">${name}${current === person1 ? ' (instance)' : '.prototype'}</span>\n`;

                if (props.length > 0 && depth > 0) {
                    props.slice(0, 3).forEach(prop => {
                        html += `${indent}  <span style="color: #dcdcaa;">${prop}</span>\n`;
                    });
                    if (props.length > 3) {
                        html += `${indent}  <span style="color: #6a9955;">... and ${props.length - 3} more</span>\n`;
                    }
                }

                current = Object.getPrototypeOf(current);
                depth++;
            }
            html += '\n<span style="color: #ce9178;">null</span>';
            output.innerHTML = html;
        }

        function addProtoMethod() {
            const output = document.getElementById('protoOutput');

            if (!Person.prototype.introduce) {
                Person.prototype.introduce = function() {
                    return `I'm ${this.name}, ${this.age} years old.`;
                };

                output.innerHTML = `<span style="color: #4ec9b0;">✅ Added introduce() to Person.prototype</span>\n\n` +
                    `<span style="color: #569cd6;">Before:</span> person1.introduce → ${typeof person1.introduce === 'undefined' ? 'undefined' : 'function'}\n\n` +
                    `<span style="color: #569cd6;">After:</span>\n` +
                    `  person1.introduce() → "${person1.introduce()}"\n` +
                    `  person2.introduce() → "${person2.introduce()}"\n\n` +
                    `<span style="color: #4ec9b0;">→ All instances get the new method!</span>`;
            } else {
                output.innerHTML = `<span style="color: #4ec9b0;">introduce() already exists on prototype</span>\n\n` +
                    `person1.introduce() → "${person1.introduce()}"`;
            }
        }

        function checkProtoEquality() {
            const output = document.getElementById('protoOutput');

            output.innerHTML =
                `<span style="color: #569cd6;">Prototype Equality Checks:</span>\n\n` +
                `  Object.getPrototypeOf(person1) === Person.prototype\n` +
                `    → <span style="color: #4ec9b0;">${Object.getPrototypeOf(person1) === Person.prototype}</span>\n\n` +
                `  person1.__proto__ === Person.prototype\n` +
                `    → <span style="color: #4ec9b0;">${person1.__proto__ === Person.prototype}</span>\n\n` +
                `  Person.prototype.isPrototypeOf(person1)\n` +
                `    → <span style="color: #4ec9b0;">${Person.prototype.isPrototypeOf(person1)}</span>\n\n` +
                `  person1 instanceof Person\n` +
                `    → <span style="color: #4ec9b0;">${person1 instanceof Person}</span>\n\n` +
                `  person1 instanceof Object\n` +
                `    → <span style="color: #4ec9b0;">${person1 instanceof Object}</span>`;
        }

        // Initialize
        showProtoChain();
    </script>

</body>
</html>

Quick Reference

What is a Prototype?

AspectDescription
DefinitionMechanism for adding properties/methods to objects
Where it livesEvery function has a prototype property
Shared byAll instances created with new
EnablesInheritance, memory efficiency, method reuse

Prototype Chain

instance
  │
  └── [[Prototype]] ──→ Constructor.prototype
                            │
                            └── [[Prototype]] ──→ Object.prototype
                                                      │
                                                      └── [[Prototype]] ──→ null

Lookup order:

  1. Object itself
  2. Its prototype
  3. Prototype’s prototype
  4. … until null

Accessing the Prototype

MethodDescriptionStatus
Object.getPrototypeOf(obj)Standard method✅ Recommended
obj.__proto__Legacy accessor⚠️ Deprecated
Constructor.prototypeConstructor’s prototype✅ Recommended
obj instanceof ConstructorCheck instance✅ Recommended
Constructor.prototype.isPrototypeOf(obj)Check chain✅ Recommended

Prototype vs Class (Modern)

// Prototype style (classic)
function Person(name) {
    this.name = name;
}
Person.prototype.greet = function() {
    return `Hi, I'm ${this.name}`;
};

// Class style (modern — same underlying mechanism)
class PersonClass {
    constructor(name) {
        this.name = name;
    }
    greet() {
        return `Hi, I'm ${this.name}`;
    }
}

Both produce the same result — classes are just syntactic sugar over prototypes!


Best Practices

Do This:

// Define methods on the prototype for memory efficiency
function Person(name) {
    this.name = name;
}
Person.prototype.greet = function() {
    return `Hi, ${this.name}`;
};

// Use Object.create for inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// Use Object.getPrototypeOf() (not __proto__)
const proto = Object.getPrototypeOf(obj);

// Use classes for modern OOP
class Person {
    constructor(name) { this.name = name; }
    greet() { return `Hi, ${this.name}`; }
}

// Check own vs inherited
if (obj.hasOwnProperty('key')) { }

Don’t Do This:

// Don't duplicate methods on every instance
function Person(name) {
    this.name = name;
    this.greet = function() { }; // ❌ Creates new function per instance!
}

// Don't use __proto__ in production
obj.__proto__; // ❌ Deprecated — use Object.getPrototypeOf()

// Don't modify built-in prototypes
Array.prototype.myMethod = function() { }; // ⚠️ Dangerous!

// Don't forget to reset constructor after inheritance
Dog.prototype = Object.create(Animal.prototype);
// Dog.prototype.constructor = Dog; // ❌ Missing — constructor points to Animal!

// Don't confuse prototype with instance
Person.greet;             // ❌ Constructor doesn't have greet
Person.prototype.greet;   // ✅ Prototype has greet
person1.greet;            // ✅ Instance inherits greet

Common Pitfalls

PitfallProblemSolution
Modifying built-insBreaks other codeAvoid; use utilities
__proto__ in codeDeprecatedUse Object.getPrototypeOf()
Forgetting constructorWrong constructor referenceReset after inheritance
Shared mutable stateAll instances affectedKeep data on instance, not prototype
Modifying prototype during iterationUnpredictableDon’t

Real-World Example

// Custom error class using prototypes
function ValidationError(message, field) {
    Error.call(this, message);
    this.name = 'ValidationError';
    this.field = field;
    this.message = message;
}

ValidationError.prototype = Object.create(Error.prototype);
ValidationError.prototype.constructor = ValidationError;

ValidationError.prototype.toString = function() {
    return `${this.name}: ${this.message} (field: ${this.field})`;
};

try {
    throw new ValidationError('Invalid email', 'email');
} catch (error) {
    console.log(error.toString());
    console.log(error instanceof ValidationError); // true
    console.log(error instanceof Error);           // true
}

Pro Tip: Prototypes are the foundation of JavaScript’s object model. Understanding them helps you:

  • Debug inheritance issues — know what’s on the instance vs prototype
  • Optimize memory — define methods on prototypes, not instances
  • Understand classes — they’re just prototype-based under the hood
  • Use Object.create() for clean inheritance
  • Avoid modifying built-in prototypes — it can break other code

Rule of thumb: Data on the instance, methods on the prototype. Use ES6 classes for modern code — they’re cleaner and do the same thing!


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!