त्रुटिरेंजत्रुटि: अधिकतम कॉल स्टैक आकार पार हो गया (गैर-ब्राउज़र परिवेश में इसे “स्टैक ओवरफ़्लो” भी कहा जाता है) का अर्थ है एक फ़ंक्शन को स्वयं (प्रत्यक्ष या अप्रत्यक्ष रूप से) इतनी बार कॉल करना कि जावास्क्रिप्ट में कॉल स्टैक स्थान समाप्त हो जाए। यहां हर कारण और समाधान है।
📋 Table of Contents
- ऐसा क्यों होता है
- कारण 1: अनंत पुनरावर्ती ईवेंट हैंडलर
- कारण 2: JSON.stringify
- में वृत्ताकार वस्तु संदर्भ कारण 3: रिकर्सन के साथ डीप ऑब्जेक्ट क्लोनिंग
- कारण 4: किसी फ़ंक्शन को संदर्भित करने के बजाय गलती से उसे कॉल करना
- फिक्स: रिकर्सन को इटरेशन (ट्रैम्पोलिनिंग) में बदलें
- स्टैक ओवरफ्लो को डीबग करना
- अक्सर पूछे जाने वाले प्रश्न
- निष्कर्ष
ऐसा क्यों होता है
प्रत्येक फ़ंक्शन कॉल कॉल स्टैक में एक फ़्रेम जोड़ता है। जावास्क्रिप्ट इंजन का स्टैक आकार सीमित होता है (ब्राउज़र/नोड संस्करण के आधार पर ~10,000-15,000 फ़्रेम)। जब पुनरावर्ती कार्यों में उचित आधार केस का अभाव होता है, या जब दो फ़ंक्शन एक-दूसरे को अनिश्चित काल तक कॉल करते हैं, तो स्टैक भर जाता है और रेंजएरर फेंकता है।
// Simplest example: missing base case
function factorial(n) {
return n * factorial(n - 1); // never stops — factorial(0) calls factorial(-1) etc.
}
factorial(5); // RangeError: Maximum call stack size exceeded
// Fix: add base case
function factorial(n) {
if (n <= 1) return 1; // base case stops recursion
return n * factorial(n - 1);
}
कारण 1: अनंत पुनरावर्ती ईवेंट हैंडलर
// 🐛 Bug: React onChange updates state, triggers re-render, fires onChange again
function SearchInput() {
const [value, setValue] = useState('');
return <input
value={value}
onChange={(e) => {
setValue(e.target.value);
// If something here triggers another state update that causes re-render...
}}
/>;
}
// 🐛 Bug: useEffect calling function that updates state without dependency guards
useEffect(() => {
setData(processData(data)); // updates data → triggers effect → updates data...
}, [data]);
// ✅ Fix: use functional update or conditional guard
useEffect(() => {
if (data.needsProcessing) {
setData(prev => processData(prev)); // functional update avoids dependency
}
}, [data.id]); // depend on stable identifier, not the object
कारण 2: JSON.stringify
// 🐛 Bug: circular reference crashes JSON.stringify
const obj = { name: "test" };
obj.self = obj; // circular reference
JSON.stringify(obj); // TypeError: Converting circular structure to JSON
// ✅ Fix 1: Replace circular references
function stringifySafe(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]';
seen.add(value);
}
return value;
});
}
// ✅ Fix 2: Use structuredClone for deep copying (handles circular refs)
const safeCopy = structuredClone(obj); // built-in, available Node 17+ / modern browsers
में वृत्ताकार वस्तु संदर्भ कारण 3: रिकर्सन के साथ डीप ऑब्जेक्ट क्लोनिंग
// 🐛 Bug: naive deep clone fails on deeply nested objects
function deepClone(obj) {
if (typeof obj !== 'object') return obj;
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, deepClone(v)]) // crashes at ~10k depth
);
}
// ✅ Fix 1: Use structuredClone (built-in, iterative internally)
const clone = structuredClone(deeplyNestedObj);
// ✅ Fix 2: Convert recursion to iteration for very deep objects
function deepCloneIterative(root) {
const stack = [{ source: root, target: Array.isArray(root) ? [] : {} }];
const result = stack[0].target;
const map = new WeakMap([[root, result]]);
while (stack.length) {
const { source, target } = stack.pop();
for (const [key, value] of Object.entries(source)) {
if (typeof value === 'object' && value !== null) {
if (map.has(value)) {
target[key] = map.get(value);
continue;
}
const newTarget = Array.isArray(value) ? [] : {};
map.set(value, newTarget);
target[key] = newTarget;
stack.push({ source: value, target: newTarget });
} else {
target[key] = value;
}
}
}
return result;
}
कारण 4: किसी फ़ंक्शन को संदर्भित करने के बजाय गलती से उसे कॉल करना
// 🐛 Bug: function immediately calls itself
class Timer {
start() {
this.intervalId = setInterval(this.tick(), 1000); // tick() CALLED, not referenced
// this.tick() returns undefined (or calls tick immediately, which schedules tick...)
}
tick() {
console.log('tick');
this.start(); // calls start() which calls tick() again immediately
}
}
// ✅ Fix: pass function reference, not call result
class Timer {
start() {
this.intervalId = setInterval(() => this.tick(), 1000); // reference, not call
}
tick() {
console.log('tick');
}
}
फिक्स: रिकर्सन को इटरेशन (ट्रैम्पोलिनिंग) में बदलें
// For recursive algorithms that process large inputs:
// 🐛 Recursive sum — crashes for large n
function sum(n) {
if (n === 0) return 0;
return n + sum(n - 1); // crashes for n > ~10,000
}
// ✅ Iterative solution
function sumIterative(n) {
let total = 0;
for (let i = n; i > 0; i--) {
total += i;
}
return total;
}
// ✅ Trampolining (for complex recursive algorithms that are hard to iterate directly)
function trampoline(fn) {
return function trampolined(...args) {
let result = fn(...args);
while (typeof result === 'function') {
result = result();
}
return result;
};
}
const sumTrampoline = trampoline(function sum(n, acc = 0) {
if (n === 0) return acc;
return () => sum(n - 1, acc + n); // returns a thunk instead of recursing
});
sumTrampoline(100000); // works fine
स्टैक ओवरफ्लो को डीबग करना
// The error stack trace shows the recursive call
// RangeError: Maximum call stack size exceeded
// at factorial (/app/math.js:2:10)
// at factorial (/app/math.js:2:10) ← same line repeated = infinite recursion
// at factorial (/app/math.js:2:10)
// ...
// Step 1: Look for the repeated line in the stack trace
// Step 2: Add console.log with a counter to see how deep it goes
let callCount = 0;
function myFunction(data) {
callCount++;
if (callCount > 1000) {
console.trace('Deep recursion detected at:', callCount);
throw new Error('Stack overflow prevention');
}
// ...
}
// Step 3: Check for circular dependencies
function detectCircular(obj, path = '', visited = new WeakSet()) {
if (typeof obj !== 'object' || obj === null) return;
if (visited.has(obj)) { console.log('Circular at:', path); return; }
visited.add(obj);
for (const [key, val] of Object.entries(obj)) {
detectCircular(val, `${path}.${key}`, visited);
}
}
अक्सर पूछे जाने वाले प्रश्न
प्रश्न: जावास्क्रिप्ट में अधिकतम कॉल स्टैक गहराई क्या है?
उ: Chrome/Node.js में ~10,000-15,000 फ़्रेम; फ़ायरफ़ॉक्स में ~5,000-10,000; इंजन संस्करण और फ़्रेम आकार के अनुसार भिन्न होता है। संस्करणों के बीच सटीक संख्याएँ बदलती रहती हैं।
प्रश्न: क्या मैं कॉल स्टैक आकार बढ़ा सकता हूँ?
ए: नोड.जेएस में:node --stack-size=65536 app.js (आकार KB में). ब्राउज़रों में: संभव नहीं. बेहतर समाधान: सीमा बढ़ाने के बजाय रिकर्सन को ठीक करें।
प्रश्न: स्टैक ओवरफ़्लो और मेमोरी से बाहर ढेर के बीच क्या अंतर है?
ए: स्टैक ओवरफ़्लो (रेंज एरर) = बहुत सारे फ़ंक्शन कॉल फ़्रेम। स्मृति से बाहर ढेर (घातक त्रुटि) = बहुत अधिक वस्तुएं आवंटित। स्टैक ओवरफ्लो गहरी पुनरावृत्ति से होता है; हीप त्रुटियाँ बहुत अधिक डेटा आवंटित करने से होती हैं।
प्रश्न: मैं 2026 में किसी वस्तु का सुरक्षित रूप से डीप क्लोन कैसे कर सकता हूँ?
ए:structuredClone(obj)का प्रयोग करें – सभी आधुनिक ब्राउज़रों और Node.js 17+ में निर्मित। यह सर्कुलर संदर्भ, दिनांक ऑब्जेक्ट, मानचित्र, सेट और ऐरेबफ़र्स को सही ढंग से संभालता है। JSON.parse(JSON.stringify(obj)) का उपयोग केवल सर्कुलर रेफरी के बिना सरल JSON-क्रमबद्ध ऑब्जेक्ट के लिए करें।
प्रश्न: क्या टेल कॉल ऑप्टिमाइज़ेशन जावास्क्रिप्ट में उपलब्ध है?
उत्तर: सैद्धांतिक रूप से हाँ (ES6 विनिर्देश में TCO शामिल है), लेकिन केवल Safari इसे 2026 में पूरी तरह से लागू करता है। V8 (Chrome/Node.js) ने TCO को हटा दिया। स्टैक सुरक्षा के लिए टीसीओ पर निर्भर न रहें – इसके बजाय पुनरावृत्त समाधान या ट्रैम्पोलिनिंग का उपयोग करें।
निष्कर्ष
अधिकतम कॉल स्टैक आकार से अधिक त्रुटियाँ अनंत या अत्यंत गहरी पुनरावृत्ति से आती हैं। समाधान कारण पर निर्भर करता है:रिकर्सन को रोकने के लिए एक बेस केस जोड़ें, बड़े इनपुट के लिए रिकर्सन को पुनरावृत्ति में परिवर्तित करें, गहरी प्रतियों के लिए संरचितक्लोन का उपयोग करें, और सर्कुलर ऑब्जेक्ट संदर्भों पर नजर रखें. स्टैक ट्रेस हमेशा अनंत पुनरावृत्ति का कारण बनने वाली दोहराई गई रेखा को दिखाता है – इसे पहले पढ़ें और फिक्स आमतौर पर स्पष्ट है।
🔗 Share this article
✍️ Leave a Comment