💡
__proto__and[[prototype]]is same
JavaScript has two different prototype concepts:
[[Prototype]](internal hidden link).prototype(property on functions)
These are NOT the same.
They work together, but serve different purposes.
Let’s break them down cleanly.
🟦 1. [[Prototype]] — Hidden internal link
Every object in JavaScript has a hidden property called [[Prototype]].
✔ What it is:
A hidden reference to another object.
Used for inheritance.
Accessed in code via
__proto__(old) orObject.getPrototypeOf()(modern).Creates the prototype chain.
✔ Example:
const animal = { eats: true };
const rabbit = { jumps: true };
Object.setPrototypeOf(rabbit, animal);
console.log(rabbit.eats); // inherited✔ Key point:
[[Prototype]] belongs to objects (including arrays, functions, everything).
🟦 2. prototype — A property ONLY on functions
This one exists only on functions (because they may be used as constructors).
✔ What it is:
An object that will become the
[[Prototype]]of all instances created usingnew.
✔ Example:
function Car(model) {
this.model = model;
}
Car.prototype.start = function() {
console.log("Starting...");
};
const c1 = new Car("BMW");Internally:
c1.[[Prototype]] === Car.prototype✔ Key point:
.prototype belongs to functions, not objects.
🎯 Summary Table
Feature |
|
|
|---|---|---|
What is it? | Hidden internal link | A normal property |
Belongs to? | All objects | Only functions |
Used for? | Inheriting properties | Setting prototype for instances of a function |
How to access? |
|
|
Affects? | Only the object itself | Objects created with |
🟩 Perfect Analogy
🔹 prototype = Blueprint
A function's .prototype is a blueprint for objects created using new.
🔹 [[Prototype]] = Pointer
Each object stores a pointer ([[Prototype]]) to the blueprint it follows.
🟧 The Two Are Connected Like This
When you do:
let c1 = new Car("BMW");JavaScript automatically sets:
c1.[[Prototype]] = Car.prototype✔ That's why .start() is found on the prototype
✔ Even though it's not inside c1
🟦 Extra Clarification for Property Descriptors
jumps: { value: true } gets stored in rabbit object itself, not prototype
Because:
Object.create(animal, {
jumps: { value: true }
});👉 descriptors always create own properties on the object you're creating
👉 not inside the prototype
<div class="infographic-protolink">
<style>
.infographic-protolink {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #1a8a5f;
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
overflow-x: hidden;
}
.infographic-protolink[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4fbf8f;
}
.infographic-protolink * { box-sizing: border-box; }
.infographic-protolink h1 { font-family: 'Fraunces', Georgia, serif; font-weight: 600; margin: 0; }
.infographic-protolink p { margin: 0; }
.infographic-protolink button { font-family: inherit; }
.pl-wrap { padding: clamp(1.25rem, 5vw, 2.5rem) clamp(1.1rem, 5vw, 3rem) 2rem; }
.pl-hero { text-align: center; margin-bottom: 1.4rem; }
.pl-hero h1 { font-size: clamp(1.35rem, 4.5vw, 1.8rem); }
.pl-hero p { color: var(--ink-soft); margin-top: 0.45rem; font-size: 0.88rem; }
.pl-diagram { position: relative; width: 100%; max-width: 520px; margin: 0 auto; }
.pl-svg { width: 100%; height: auto; display: block; overflow: visible; }
.pl-path { fill: none; stroke: var(--line); stroke-width: 2.5; stroke-dasharray: 8 6; }
.pl-path.is-drawn { stroke: var(--accent); stroke-dasharray: none; transition: stroke 0.3s ease; }
.pl-path.animate-in { stroke-dasharray: 400; stroke-dashoffset: 400; animation: plDraw 0.9s ease forwards; }
@keyframes plDraw { to { stroke-dashoffset: 0; } }
.pl-node { position: absolute; text-align: center; padding: 0.5rem 0.7rem; border: 1.5px solid var(--line); background: var(--paper-alt); transform: translate(-50%, -50%); width: 150px; transition: opacity 0.4s ease, transform 0.4s ease, border-color 0.4s ease, background 0.4s ease; }
.pl-node .pl-name { font-family: 'IBM Plex Mono', monospace; font-weight: 700; font-size: 0.76rem; }
.pl-node .pl-sub { font-size: 0.6rem; color: var(--ink-soft); margin-top: 0.15rem; }
.pl-node.fn { border-color: var(--accent); background: var(--accent-soft); }
.pl-node.target { width: 190px; }
.pl-node.target.is-pulse { animation: plPulse 0.6s ease; border-color: var(--accent); }
@keyframes plPulse { 0% { transform: translate(-50%, -50%) scale(1); } 40% { transform: translate(-50%, -50%) scale(1.06); } 100% { transform: translate(-50%, -50%) scale(1); } }
.pl-node.instance { opacity: 0; transform: translate(-50%, -60%) scale(0.85); }
.pl-node.instance.is-shown { opacity: 1; transform: translate(-50%, -50%) scale(1); border-color: var(--accent); }
.pl-label { position: absolute; font-family: 'IBM Plex Mono', monospace; font-size: 0.62rem; color: var(--ink-soft); background: var(--paper); padding: 0.05rem 0.3rem; white-space: nowrap; transition: color 0.3s ease, opacity 0.3s ease; }
.pl-label.is-lit { color: var(--accent); font-weight: 600; }
.pl-play { display: block; margin: 1.1rem auto 0; border: none; background: var(--accent); color: #fff; padding: 0.5rem 1.2rem; font-size: 0.8rem; font-weight: 600; cursor: pointer; }
.pl-play:active { transform: scale(0.96); }
.pl-play:disabled { opacity: 0.4; cursor: default; }
.pl-result {
margin: 1rem auto 0; max-width: 340px; text-align: center;
border: 1.5px solid var(--line); background: var(--paper-alt); padding: 0.5rem 0.8rem;
font-family: 'IBM Plex Mono', monospace; font-size: 0.72rem;
opacity: 0; transform: translateY(6px); transition: opacity 0.35s ease, transform 0.35s ease, border-color 0.35s ease;
}
.pl-result.is-shown { opacity: 1; transform: translateY(0); border-color: var(--good); color: var(--good); }
.pl-legend { display: flex; justify-content: center; gap: 1.6rem; flex-wrap: wrap; margin-top: 1.3rem; font-size: 0.76rem; color: var(--ink-soft); }
.pl-legend span strong { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
.infographic-protolink *, .infographic-protolink *::before, .infographic-protolink *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="pl-wrap">
<div class="pl-hero">
<h1>.prototype and [[Prototype]] meet at the same object</h1>
<p>One is the function's own property. The other is the instance's hidden link. Build an instance and watch them land in the same place.</p>
</div>
<div class="pl-diagram">
<svg class="pl-svg" viewBox="0 0 500 250">
<path class="pl-path is-drawn" id="pathFn" d="M120,60 L235,175"></path>
<path class="pl-path" id="pathInst" d="M380,60 L265,175"></path>
</svg>
<div class="pl-node fn" style="left: 24%; top: 24%;">
<div class="pl-name">function Person</div>
<div class="pl-sub">the constructor</div>
</div>
<div class="pl-node instance" id="plInstance" style="left: 76%; top: 24%;">
<div class="pl-name">p1 = new Person()</div>
<div class="pl-sub">the instance</div>
</div>
<div class="pl-label" id="labelFn" style="left: 27%; top: 44%;">.prototype</div>
<div class="pl-label" id="labelInst" style="left: 68%; top: 44%;">[[Prototype]]</div>
<div class="pl-node target" id="plTarget" style="left: 50%; top: 76%;">
<div class="pl-name">Person.prototype</div>
<div class="pl-sub">constructor, sayHello…</div>
</div>
</div>
<button class="pl-play" id="plPlay" type="button">new Person()</button>
<div class="pl-result" id="plResult">p1.__proto__ === Person.prototype → true</div>
<div class="pl-legend">
<span><strong>.prototype</strong> — function's own property, exists from the start</span>
<span><strong>[[Prototype]]</strong> — instance's hidden link, set at creation</span>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-protolink')
|| document.querySelector('.infographic-protolink');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const playBtn = wrapper.querySelector('#plPlay');
const instance = wrapper.querySelector('#plInstance');
const pathInst = wrapper.querySelector('#pathInst');
const labelInst = wrapper.querySelector('#labelInst');
const target = wrapper.querySelector('#plTarget');
const result = wrapper.querySelector('#plResult');
playBtn.addEventListener('click', () => {
instance.classList.remove('is-shown');
pathInst.classList.remove('is-drawn', 'animate-in');
labelInst.classList.remove('is-lit');
target.classList.remove('is-pulse');
result.classList.remove('is-shown');
void instance.offsetWidth;
setTimeout(() => instance.classList.add('is-shown'), 60);
setTimeout(() => {
pathInst.classList.add('animate-in');
labelInst.classList.add('is-lit');
}, 500);
setTimeout(() => {
pathInst.classList.add('is-drawn');
target.classList.add('is-pulse');
}, 1350);
setTimeout(() => result.classList.add('is-shown'), 1500);
playBtn.disabled = true;
setTimeout(() => { playBtn.disabled = false; }, 1700);
});
})();
</script>