چرا باید به جای JSON.stringify و {obj...} از structuredClone استفاده کنیم؟

چرا باید به جای JSON.stringify و {obj...} از structuredClone استفاده کنیم؟

تیر ۰۴, ۱۴۰۵

structuredClone() چیست؟

structuredClone() یک تابع سراسری (Global Function) است که در سال ۲۰۲۲ معرفی شد و امکان کلونینگ عمیق (Deep Cloning) اشیاء در جاوااسکریپت را فراهم می‌کند. برخلاف روش‌های سنتی مانند JSON.stringify() و JSON.parse() که با ساختارهای پیچیده و مراجع چرخه‌ای (Circular References) با مشکل مواجه می‌شوند، structuredClone() این چالش‌ها را به‌راحتی مدیریت می‌کند.

چرا این ویژگی یک نقطه عطف محسوب می‌شود؟

این تابع ابزاری قدرتمند برای ایجاد کلون‌های عمیق واقعی است که یکپارچگی (Integrity) اشیاء تودرتو و مراجع چرخه‌ای را بدون نیاز به منطق اضافی یا راه‌حل‌های موقت حفظ می‌کند. علاوه بر این، در محیط‌های مدرن وب از جمله Web Workers نیز در دسترس است.


۱. کلونینگ اشیاء ساده: مبانی

استفاده از `

const original = { name: "Alice", details: { age: 25 } };
const shallowCopy = { ...original };

shallowCopy.details.age = 30;

console.log(original.details.age); // 30
console.log(shallowCopy.details.age); // 30

چه اتفاقی افتاد؟

عملگر گسترش (Spread Operator) یعنی {...obj} فقط یک کپی سطحی (Shallow Copy) می‌سازد. شیء details به‌صورت عمیق کلون نمی‌شود؛ بنابراین تغییرات اعمال‌شده در shallowCopy.details بر شیء details اصلی نیز اثر می‌گذارد.

استفاده از JSON.stringify() + JSON.parse() (کپی عمیق)

const original = { name: "Alice", details: { age: 25 } };
const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.details.age = 30;

console.log(original.details.age); // 25
console.log(deepCopy.details.age); // 30

چه اتفاقی افتاد؟

این روش یک کپی عمیق می‌سازد، اما محدودیت‌هایی دارد: نمی‌تواند توابع، مقدار undefined یا مراجع چرخه‌ای را مدیریت کند.

استفاده از structuredClone() (کپی عمیق)

const original = { name: "Alice", details: { age: 25 } };
const clone = structuredClone(original);

clone.details.age = 30;

console.log(original.details.age); // 25
console.log(clone.details.age); // 30

چه اتفاقی افتاد؟

structuredClone() یک کپی عمیق واقعی می‌سازد که ساختار داده را بدون محدودیت‌های JSON.stringify() حفظ می‌کند و توانایی مدیریت مراجع چرخه‌ای، انواع داده‌های خاص و مقادیر undefined را دارد.


۲. مدیریت مراجع چرخه‌ای: یک چالش اساسی

مرجع چرخه‌ای با `

const original = { name: "Alice" };
original.self = original;

// کپی سطحی صرفاً همان مرجع را کپی می‌کند و حلقه مرجع باقی می‌ماند:
const shallowCopy = { ...original };
console.log(shallowCopy.self === original); // true

چه اتفاقی افتاد؟

عملگر {...obj} کلونینگ عمیق انجام نمی‌دهد؛ بلکه فقط ارجاع به شیء اصلی را در فیلد self کپی می‌کند و مرجع چرخه‌ای را تفکیک نمی‌کند.

مرجع چرخه‌ای با JSON.stringify()

const original = { name: "Alice" };
original.self = original;

// این دستور باعث خطا می‌شود:
const jsonCopy = JSON.parse(JSON.stringify(original)); // TypeError: Converting circular structure to JSON

چه اتفاقی افتاد؟

متد JSON.stringify() قابلیت پیمایش مراجع چرخه‌ای را ندارد و با پرتاب خطای TypeError متوقف می‌شود.

مرجع چرخه‌ای با structuredClone()

const original = { name: "Alice" };
original.self = original;

const clone = structuredClone(original);

console.log(clone !== original); // true
console.log(clone.self === clone); // true

چه اتفاقی افتاد؟

structuredClone() مراجع چرخه‌ای را به‌دقت مدیریت می‌کند و یک کپی عمیق کاملاً مستقل و معتبر بدون هیچ‌گونه خطا می‌سازد.


۳. کلونینگ با توابع و undefined: یک آزمون دیگر

استفاده از `

const original = { name: "Alice", greet: () => "Hello!", value: undefined };
const shallowCopy = { ...original };

console.log(shallowCopy.greet()); // "Hello!"
console.log(shallowCopy.value); // undefined

چه اتفاقی افتاد؟

عملگر {...obj} توابع و undefined را طبق انتظار کپی می‌کند، اما این کپی صرفاً سطحی است.

استفاده از JSON.stringify()

const original = { name: "Alice", greet: () => "Hello!", value: undefined };
const jsonCopy = JSON.parse(JSON.stringify(original));

console.log(jsonCopy.greet); // undefined
console.log(jsonCopy.value); // undefined

چه اتفاقی افتاد؟

JSON.stringify() توانایی سریال‌سازی توابع یا مقادیر undefined را در ساختار شیء ندارد؛ در نتیجه این ویژگی‌ها در خروجی حذف می‌شوند.

استفاده از structuredClone()

const original = { name: "Alice", greet: () => "Hello!", value: undefined };
// توجه: structuredClone توابع را کلون نمی‌کند و خطای DataCloneError پرتاب می‌کند.
// اما برای مقادیر undefined کاملاً پشتیبانی دارد:
const objWithUndefined = { name: "Alice", value: undefined };
const clone = structuredClone(objWithUndefined);

console.log(clone.value); // undefined
console.log("value" in clone); // true

چه اتفاقی افتاد؟

structuredClone() توابع را کلون نمی‌کند (و در صورت وجود تابع، خطای DataCloneError می‌دهد)، اما مقدار undefined را برعکس JSON.stringify() با حفظ کلید آن نگه‌می‌دارد. همین موضوع آن را برای ساختارهای داده‌ای پیچیده بسیار قابل اعتمادتر می‌کند.


۴. سرعت و کارایی: بررسی عملکرد

کارایی با ساختارهای داده بزرگ

const largeArray = new Array(1e6).fill({ key: "value" });

console.time("structuredClone");
const clone = structuredClone(largeArray);
console.timeEnd("structuredClone");

console.time("JSON.stringify + JSON.parse");
const jsonCopy = JSON.parse(JSON.stringify(largeArray));
console.timeEnd("JSON.stringify + JSON.parse");

چه اتفاقی افتاد؟

structuredClone() برای داده‌های بزرگ و پیچیده پیاده‌سازی بهینه‌تری در سطح مرورگر دارد و از بار محاسباتی تبدیل شیء به رشته متنی و سپس پارس مجدد آن جلوگیری می‌کند.


۵. نتیجه‌گیری: چرا structuredClone() استاندارد آینده است؟

  • قابل اعتماد: مراجع چرخه‌ای، انواع داده‌های توکار (مانند Map، Set، Date و RegExp) و مقادیر undefined را به‌طور دقیق و پیش‌بینی‌پذیر مدیریت می‌کند.
  • کارآمد: برای مجموعه‌داده‌های حجیم، کلونینگ عمیق را با کارایی بهتری به انجام می‌رساند و نیازی به راهکارهای پیچیده و کتابخانه‌های جانبی ندارد.
  • یکپارچه و ساده: یک متد استاندارد برای رفع تمام نیازهای کپی عمیق است؛ بنابراین نیازی به اتکا بر {...obj}، ترکیب‌های غیراستاندارد JSON یا توابع بازگشتی دست‌نویس نخواهید داشت.

خلاصه: متد structuredClone() یک راهکار مدرن، استاندارد و مطمئن برای کلونینگ عمیق اشیاء در جاوااسکریپت است که محدودیت‌های ساختاری روش‌های گذشته مانند JSON.stringify() را برطرف کرده و انواع داده‌های پیشرفته و مراجع چرخه‌ای را با ساختار درست مدیریت می‌کند.