Chapter 1 of ?
js 10 min read

JavaScript Mastery — Chapter 22: Advanced JS Objects & Prototypes

Module 5: Advanced Functions & OOP Chapter 22 28 min read

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.

adminUser (Instance) name: "Alice" role: "SUPERADMIN" [[Prototype]] ──┐ Own properties live here User.prototype login() { ... } logout() { ... } [[Prototype]] ──┐ Shared behavior delegates here Object.prototype hasOwnProperty() toString() [[Prototype]]: null End of the chain (Root)

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 actual link on EVERY object instance

The internal pointer that every object possesses, pointing to the prototype object it delegates to. You inspect it via Object.getPrototypeOf(obj).

ConstructorFunction.prototype
The blueprint on constructor FUNCTIONS only

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()
No new properties allowed.
Existing properties can be modified or deleted. Object.isExtensible(obj) returns false.
2. Object.seal()
No adding or deleting properties.
Sets configurable: false on all properties. Existing writable properties can still be modified.
3. Object.freeze()
Total shallow lockdown.
Sets 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:

Deep Freeze Requirements
  1. Handle circular references without entering an infinite call stack loop using a WeakSet.
  2. Recursively traverse and freeze all child objects, arrays, and functions.
  3. Freeze the parent object and return it.
Production Deep Freeze Implementation
/**
 * 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.

1. What is the terminal (end) value of JavaScript's prototype chain?
undefined
null (at Object.prototype.[[Prototype]])
The global window object
Function.prototype
2. What are the default values of writable, enumerable, and configurable when creating a property via Object.defineProperty()?
All true
All false
writable: true, others false
undefined
3. What is the difference between Object.freeze() and Object.seal()?
freeze() works recursively, while seal() is shallow.
In 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.
There is no difference; they are synonyms.
4. Why is modern Object.hasOwn(obj, prop) preferred over obj.hasOwnProperty(prop)?
It safely handles objects created with Object.create(null) which do not inherit from Object.prototype, and avoids property shadowing bugs.
It searches prototypes all the way to the root.
It converts keys to numbers automatically.
It is asynchronous and returns a Promise.
5. What does Object.create(proto) do?
It clones all properties of proto by performing a deep copy.
It creates a new empty object whose internal [[Prototype]] is set directly to proto.
It converts proto into a constructor function.
It freezes proto and prevents any further inheritance.
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up
—

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *
★ ★ ★ ★ ★