Implement JavaScript’s closure mechanism with environment chains and prototype-based inheritance with efficient property lookup
Master two fundamental JavaScript features: closures that capture lexical scope and prototype chains that enable object-oriented programming without classes.
Closures and prototypes are essential to JavaScript’s identity as a language. Closures enable powerful functional programming patterns, while prototypes provide a flexible inheritance mechanism.
Understanding closures and prototypes deeply is crucial for implementing a JavaScript engine and writing efficient JavaScript code.
Closures allow functions to access variables from their lexical scope even after the outer function has returned. This is implemented using environment chains.
JavaScript uses prototype-based inheritance instead of class-based inheritance. Every object has a prototype, and property lookups traverse the prototype chain.
// Every object has a [[Prototype]] (accessed via __proto__)const obj = { x: 1 };console.log(obj.toString); // Inherited from Object.prototype// Constructor functions and prototypesfunction Person(name) { this.name = name;}Person.prototype.greet = function() { console.log(`Hello, I'm ${this.name}`);};const alice = new Person('Alice');alice.greet(); // "Hello, I'm Alice"// greet is found in Person.prototype
dog object├── name: "Max" (own property)├── breed: "Labrador" (own property)└── [[Prototype]] → Dog.prototype ├── bark: function └── [[Prototype]] → Animal.prototype ├── eat: function └── [[Prototype]] → Object.prototype ├── toString: function ├── valueOf: function └── [[Prototype]] → null
When accessing a property:
Check own properties
If not found, check [[Prototype]]
Repeat step 2 up the chain
If reaches null, return undefined
dog.name // Found in own propertiesdog.bark // Found in Dog.prototypedog.eat // Found in Animal.prototypedog.toString // Found in Object.prototypedog.missing // Not found anywhere → undefined
Property assignment behavior:
Always creates/updates own property
Never modifies prototype properties
Exception: setter in prototype is called
dog.eat = "cat food"; // Creates own property 'eat'console.log(dog.eat); // "cat food" (own property)console.log(Object.getPrototypeOf(dog).eat); // function (unchanged)
Closures keep their entire environment alive, which can prevent garbage collection:
function createHandler() { const largeData = new Array(1000000); // This closure captures entire environment including largeData return function() { console.log('Handler called'); };}// Better: explicitly null unused variablesfunction createHandler() { const largeData = new Array(1000000); // Use largeData... largeData = null; // Allow GC return function() { console.log('Handler called'); };}
Prototype pollution
Modifying Object.prototype affects all objects:
// NEVER do this!Object.prototype.myMethod = function() { /* ... */ };// Now ALL objects have myMethodconst obj = {};console.log(obj.myMethod); // Exists!// Can cause security issues and break code
Shadowing prototype properties
Own properties shadow prototype properties:
function Person(name) { this.name = name;}Person.prototype.greet = function() { console.log(`Hello, I'm ${this.name}`);};const person = new Person('Alice');person.greet = 'not a function'; // Shadows prototype methodperson.greet(); // TypeError: person.greet is not a function