Explain the prototype chain and how Razorpay's SDK can extend native objects without breaking merchant code.
Every object has a hidden [[Prototype]] pointer. Property lookup walks this chain. SDKs that need to extend native types do so safely by **subclassing or adding namespaced static helpers**, not patching `Array.prototype` etc. — that pollutes globals and can collide with merchant code. The safe API gives consumers an SDK object whose own prototype chain is private.
Prototype chain basics
const arr = [1, 2, 3];
arr.__proto__ === Array.prototype; // true
Array.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // top of chainProperty lookup on arr.something:
- Own properties of
arr. Array.prototype.Object.prototype.null→ returnsundefined.
Inheritance via prototype
class Razorpay {
open() { ... }
}
class CheckoutRazorpay extends Razorpay {
customMethod() { ... }
}
const c = new CheckoutRazorpay();
c.open(); // inherited via prototype chain
c.customMethod(); // own subclass methodThe wrong way for an SDK to "extend natives"
// DON'T
Array.prototype.toRazorpayList = function () { /* ... */ };Why this is bad:
- Pollutes globals. Merchant code may rely on
for (const k in arr)not yielding library methods. - Collisions. Two libs add the same method with different semantics.
- Future-proofing. A new ECMAScript proposal may define the same name; SDK breaks or shadows it.
The right way for an SDK
- Subclass when extending.
``js class RazorpayArray extends Array { toRazorpayList() { /* ... */ } } ``
- Static helpers in the SDK namespace.
``js import { Razorpay } from "razorpay"; Razorpay.utils.formatAmount(99.5, "INR"); ``
- Symbol-keyed extensions if you must put it on existing objects.
``js const KEY = Symbol("razorpay.id"); obj[KEY] = "rzp_123"; // doesn't collide with string keys ``
- Encapsulate state in an SDK instance.
``js const rp = new Razorpay({ key }); rp.checkout.open(opts); // no global mutation ``
How property lookup interacts with extension
If the SDK monkey-patched Array.prototype.sort, every [].sort() in the merchant's codebase goes through SDK code — fragile, opaque debugging, version drift.
Modern best practice
- Ship a class-based or factory-based SDK.
- Avoid
Object.prototype/Array.prototypemutations entirely. - Lock down with
Object.freeze(Razorpay)to prevent merchants from patching the SDK. - Use TypeScript / declaration files so merchant code knows the SDK surface.
Property lookup performance
The JIT optimizes obj.x lookups via inline caches keyed on the hidden class / shape. A monkey-patched prototype changes the shape of every instance — slow path. Another reason not to patch natives.
Interview framing
"Every object has a prototype pointer; property lookup walks the chain. SDKs that want to add behavior should never patch Array.prototype or Object.prototype — they pollute the global namespace, collide with other libs and future ECMAScript additions, and slow down the engine by invalidating inline caches in merchant code. The safe shape is a class-based SDK (new Razorpay({key}).checkout.open()), with extensions as subclasses, static helpers, or Symbol-keyed properties when adding to existing objects. The chain itself is just a mechanism — what matters is keeping your library's surface isolated from globals."
Follow-up questions
- •What's a hidden class / inline cache?
- •Compare class inheritance with Object.create.
- •Why are Symbol keys safe for extending?
Common mistakes
- •Patching Array/Object.prototype in a library.
- •for-in loops over arrays (sees inherited keys).
- •Confusing __proto__ with .prototype (instance vs constructor).
Performance considerations
- •Patching native prototypes invalidates inline caches across the page — measurable slowdowns.
Edge cases
- •Object.create(null) makes a prototype-less object.
- •Subclassing Array — `length` and indexed access subtleties.
- •Frozen prototypes prevent extension entirely.
Real-world examples
- •MooTools (old library that patched natives) vs jQuery (kept its surface separate). Modern SDKs: Stripe, Razorpay, Auth0 — all class-based.