الخطوات
-
1
افهم أساسيات الـ API
يخزن
localStorageكل شيء كنص. لحفظ الكائنات أو المصفوفات يجب تسلسلها بـJSON.stringifyعند الكتابة وفكّ تسلسلها بـJSON.parseعند القراءة. القيم التي لا يمكن تسلسلها (الدوال،undefined، المراجع الدائرية) تُحذف أو تُحوَّل بصمت.javascript// Write localStorage.setItem('theme', 'dark'); localStorage.setItem('user', JSON.stringify({ name: 'Alice', age: 30 })); // Read const theme = localStorage.getItem('theme'); // 'dark' or null const user = JSON.parse(localStorage.getItem('user')); // object or null // Delete one key localStorage.removeItem('theme'); // Wipe everything (careful in production) localStorage.clear(); -
2
اعرف متى يُعطب JSON.parse
JSON.parse(null)يُعيدnullبأمان — يُعيدgetItemالقيمةnullللمفاتيح المفقودة، وهي تُحوَّل إلى النص"null"الذي يُحلَّل بلا مشكلة. لكنJSON.parse(undefined)يُطلقSyntaxError، وكذلك تحليل نص تالف. لا تقرأ أبداً بدون حماية.javascript// Safe — null is valid input for JSON.parse JSON.parse(null); // → null JSON.parse('null'); // → null // These crash: JSON.parse(undefined); // SyntaxError: Unexpected token u JSON.parse(''); // SyntaxError: Unexpected end of JSON input JSON.parse('{bad json'); // SyntaxError -
3
اكتب دالة getItem آمنة
غلّف عمليات القراءة في
try/catchوقدّم قيمة افتراضية. يعالج هذا المفاتيح المفقودة، والبيانات التالفة، والحالة النادرة حين يكونlocalStorageغير متاح (التصفح الخاص في بعض المتصفحات، أو سياسات الأمان).javascriptfunction getItem(key, defaultValue = null) { try { const raw = localStorage.getItem(key); if (raw === null) return defaultValue; return JSON.parse(raw); } catch { return defaultValue; } } // Usage const prefs = getItem('user-prefs', { theme: 'light', lang: 'en' }); const count = getItem('visit-count', 0); -
4
اكتب دالة setItem آمنة
يُطلق
localStorage.setItemخطأQuotaExceededErrorعند الوصول إلى حد 5 ميجابايت. بدون حماية، سيُعطب تطبيقك بصمت. غلّف فيtry/catch— على الأقل سجّل الفشل حتى تعرف أنه حدث.javascriptfunction setItem(key, value) { try { localStorage.setItem(key, JSON.stringify(value)); } catch (err) { if (err.name === 'QuotaExceededError') { console.warn('localStorage quota exceeded. Consider evicting old data.'); // Optional: remove the oldest entry and retry } else { console.error('localStorage write failed:', err); } } } // Usage setItem('user-prefs', { theme: 'dark', lang: 'ar' }); setItem('cart', [{ id: 1, qty: 2 }, { id: 5, qty: 1 }]); -
5
احترم حد 5 ميجابايت
الحد مشترك لكل الـ origin وعبر جميع المفاتيح. تُستنفد الـ JSON الكبيرة (الصور مشفرة كـ base64، ردود الـ API الكاملة) بسرعة. خزّن فقط ما تحتاجه عبر الجلسات — معرّفات، وتفضيلات، ونصوص قصيرة. للتخزين الأكبر في وضع عدم الاتصال، استخدم IndexedDB.
javascript// Rough size check before writing function estimatedSize(value) { return new Blob([JSON.stringify(value)]).size; // Bytes } // Check current total usage function storageUsed() { let total = 0; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); total += (key.length + (localStorage.getItem(key) ?? '').length) * 2; // UTF-16 } return total; } console.log(`Storage used: ${(storageUsed() / 1024).toFixed(1)} KB`); -
6
لا تخزن الأسرار أو الرموز المميزة
localStorageمتاح لأي JavaScript تعمل على الصفحة — بما فيها السكريبتات الخارجية، والإعلانات، وحقن XSS. لا تخزن أبداً رموز المصادقة، وجلسات cookies، والكلمات السرية، ومفاتيح الـ API، أو البيانات الشخصية فيه. استخدم cookies نوعHttpOnlyلرموز المصادقة بدلاً من ذلك؛ يُرسلها المتصفح تلقائياً ولا تستطيع JavaScript قراءتها. -
7
استمع للتغييرات عبر التبويبات بحدث storage
يُطلق حدث
storageفي كل تبويب مفتوح آخر حين يتغير مفتاح — لا في التبويب الذي أجرى التغيير. استخدمه لمزامنة الحالة (الثيم، المصادقة، السلة) عبر التبويبات بدون استطلاع.javascriptwindow.addEventListener('storage', (event) => { // event.key — the key that changed (null if clear() was called) // event.oldValue — previous value (string) // event.newValue — new value (string, or null if removed) // event.storageArea — the storage object that changed // event.url — URL of the page that made the change if (event.key === 'theme') { const theme = JSON.parse(event.newValue ?? 'null') ?? 'light'; document.documentElement.setAttribute('data-theme', theme); console.log(`Theme synced from another tab: ${theme}`); } if (event.key === null) { // localStorage.clear() was called — reset all state resetAppState(); } });
نصائح ومحاذير
- بادئ مفاتيحك بمساحة اسم التطبيق (مثل <code>myapp:theme</code>) لتجنب التعارض مع سكريبتات خارجية أو مشاريع أخرى على نفس الـ origin.
- <code>sessionStorage</code> له نفس الـ API لكنه يُمسح عند إغلاق التبويب — استخدمه للبيانات التي لا يجب أن تستمر بعد الجلسة.
- نسّخ مخطط بياناتك المخزنة دائماً. أضف حقل <code>_version</code> وهاجر أو امسح عند عدم التطابق بدلاً من الانهيار على بنيانات غير متوقعة.
- في اختبارات الوحدة، استبدل <code>localStorage</code> بكائن بسيط أو بحزمة <code>jest-localstorage-mock</code> — لا تعتمد على التخزين الحقيقي في الاختبارات.
- لا يُطلق حدث <code>storage</code> في نفس التبويب الذي أجرى التغيير — فقط في التبويبات الأخرى. لا تستخدمه لمزامنة داخل نفس التبويب.
خاتمة
localStorage مفيد تحديداً لبساطته — لكن هذه البساطة تخفي بعض أنماط الفشل الحقيقية. استخدم الدوال المساعدة الآمنة getItem/setItem، وأبقِ البيانات صغيرة وغير حساسة، وستحصل على طبقة حفظ موثوقة على جانب العميل بلا اعتماديات وبلا إعداد.