Advanced JS Objects & Prototypes
Demystify JavaScript's unique prototypal inheritance mechanism. Explore the prototype chain, the distinction between .prototype and [[Prototype]], Object.create() delegation, property descriptors (writable, enumerable, configurable), getters/setters, and immutability controls (preventExtensions, seal, freeze).
The Prototype Chain & Delegation Architecture
JavaScript does not copy methods between classes; instead, objects are linked together in a Prototype Chain. When accessing a property, the V8 engine ascends the chain until the property is found or null is reached.
1. Prototypes Demystified: .prototype vs [[Prototype]]
One of the most frequent points of confusion in JavaScript is the difference between an object's prototype linkage and a function's prototype property:
[[Prototype]] (or __proto__)
The internal pointer that every object possesses, pointing to the prototype object it delegates to. You inspect it via Object.getPrototypeOf(obj).
ConstructorFunction.prototype
A regular property that exists only on functions that will serve as constructors. When you invoke new MyFunction(), the newly minted object's [[Prototype]] is linked to this object.
Linking Prototypes with Object.create()
const vehiclePrototype = {
drive() {
return `${this.model} is driving on highway...`;
}
};
// Pure prototypal delegation without constructor functions:
const electricCar = Object.create(vehiclePrototype);
electricCar.model = 'Tesla Model 3';
console.log(electricCar.drive()); // "Tesla Model 3 is driving on highway..."
// Verify the prototype link:
console.log(Object.getPrototypeOf(electricCar) === vehiclePrototype); // true
console.log(Object.hasOwn(electricCar, 'model')); // true (Own property)
console.log(Object.hasOwn(electricCar, 'drive')); // false (Inherited from prototype!)
Try it in Playground
2. Property Descriptors & Metaprogramming
Properties in JavaScript are not just plain keys; behind the scenes, each property is controlled by an internal Property Descriptor with 4 attributes:
| Attribute | Type | Description | Default (via defineProperty) |
|---|---|---|---|
value |
Any | The actual value retrieved when reading the property. | undefined |
writable |
Boolean | If false, the value cannot be modified with an assignment operator. |
false |
enumerable |
Boolean | If false, hidden from for...in loops and Object.keys(). |
false |
configurable |
Boolean | If false, cannot be deleted and descriptor attributes cannot be altered. |
false |
Defining Immutable & Hidden Properties
const bankAccount = {
accountNumber: 'ACC-9821'
};
// Define an unmodifiable, hidden, non-deletable property:
Object.defineProperty(bankAccount, 'ssn', {
value: '***-**-8491',
writable: false, // Read-only
enumerable: false, // Won't show up in Object.keys() or JSON.stringify()
configurable: false // Cannot be deleted
});
// Verification:
console.log(bankAccount.ssn); // "***-**-8491"
console.log(Object.keys(bankAccount)); // ['accountNumber'] (ssn is hidden!)
bankAccount.ssn = '000-00-0000'; // Silently fails (or throws TypeError in strict mode)
console.log(bankAccount.ssn); // Still "***-**-8491"
Try it in Playground
3. Immutability Levels: Prevent Extensions, Seal, and Freeze
JavaScript provides 3 progressive tiers of object protection:
1. Object.preventExtensions()
Object.isExtensible(obj) returns false.2. Object.seal()
configurable: false on all properties. Existing writable properties can still be modified.3. Object.freeze()
writable: false and configurable: false. Properties cannot be added, changed, or removed.Interactive Mini-Lab: Prototype Chain Traversal
Select a JavaScript value below and walk up its prototype hierarchy in real time, inspecting what methods are inherited at every stage until reaching terminal null:
Hands-on Challenge: Building a Bulletproof Deep Freeze
Coding Challenge
Standard Object.freeze() is shallow; nested child objects remain completely mutable. Write a production-ready deepFreeze(obj) recursive function that safely locks down complex configurations:
- Handle circular references without entering an infinite call stack loop using a
WeakSet. - Recursively traverse and freeze all child objects, arrays, and functions.
- Freeze the parent object and return it.
/**
* Recursively freezes an object and all nested children, circular-safe.
*/
function deepFreeze(obj, seen = new WeakSet()) {
// Primitives or null cannot and do not need to be frozen:
if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
return obj;
}
// Prevent infinite loops on circular references:
if (seen.has(obj)) {
return obj;
}
seen.add(obj);
// Retrieve all property names (including non-enumerable symbols/keys):
const propNames = Reflect.ownKeys(obj);
for (const name of propNames) {
const value = obj[name];
if (value && (typeof value === 'object' || typeof value === 'function')) {
deepFreeze(value, seen);
}
}
return Object.freeze(obj);
}
// Verification:
const appConfig = {
api: { url: 'https://api.aicodelab.tech', timeout: 5000 },
roles: ['admin', 'editor']
};
deepFreeze(appConfig);
appConfig.api.timeout = 9999; // Silently fails or throws in strict mode!
console.log('Timeout remains locked:', appConfig.api.timeout); // 5000
Chapter 22 Knowledge Check
Validate your mastery of prototypal inheritance, descriptors, and object security.
undefined
null (at Object.prototype.[[Prototype]])
window object
Function.prototype
writable, enumerable, and configurable when creating a property via Object.defineProperty()?true
false
writable: true, others false
undefined
Object.freeze() and Object.seal()?freeze() works recursively, while seal() is shallow.
seal(), existing properties can still be modified if they were writable; in freeze(), existing properties cannot be modified at all.
seal() allows deleting properties, while freeze() does not.
Object.hasOwn(obj, prop) preferred over obj.hasOwnProperty(prop)?Object.create(null) which do not inherit from Object.prototype, and avoids property shadowing bugs.
Object.create(proto) do?proto by performing a deep copy.
[[Prototype]] is set directly to proto.
proto into a constructor function.
proto and prevents any further inheritance.