تخطَّ إلى المحتوى

التخزين المحلي في JavaScript: localStorage

الدرس 25 من 28 · JavaScript

كل ما بنيته حتى الآن ينسى كل شيء عند تحديث الصفحة: العدّاد يعود إلى الصفر، والقائمة تفرغ. السبب أن المتغيرات تعيش في الذاكرة ما دامت الصفحة مفتوحة فقط. في هذا الدرس ستتعلم التخزين المحلي (localStorage)، وبه تحفظ بيانات صغيرة في متصفح المستخدم فتبقى بعد التحديث وبعد إغلاق المتصفح.

هو مخزن صغير يعطيه المتصفح لكل موقع، يحفظ فيه أزواجاً من مفتاح وقيمة، مثل كائن بسيط.

تخيّل خزانة الأمانات في النادي: لكل عضو خزانة باسمه، يضع فيها أغراضه ويغادر، وعندما يعود في يوم آخر يجدها كما تركها. ولا يستطيع عضو أن يفتح خزانة غيره. وهكذا المواقع: لكل موقع مخزنه، ولا يقرأ موقع مخزن موقع آخر.

والبيانات تُحفظ على جهاز المستخدم نفسه، ولا تُرسل إلى أي خادم.

localStorage.setItem("username", "سارة");
const savedName = localStorage.getItem("username");
console.log(savedName);
console.log(localStorage.getItem("age"));
الناتج
سارة
null
  • setItem(المفتاح, القيمة): تحفظ القيمة. وإذا كان المفتاح موجوداً استبدلت قيمته.
  • getItem(المفتاح): تُرجع القيمة المحفوظة، أو null إذا لم يُحفظ شيء بهذا المفتاح.

حدّث الصفحة الآن واقرأ username من جديد: ستجد القيمة ما زالت موجودة.

localStorage.setItem("color", "blue");
localStorage.setItem("lang", "ar");
console.log(localStorage.length);
localStorage.removeItem("color");
console.log(localStorage.getItem("color"));
localStorage.clear();
console.log(localStorage.length);
الناتج
3
null
0
  • removeItem(المفتاح): تحذف مفتاحاً واحداً.
  • clear(): تحذف كل ما حفظه موقعك.
  • length: عدد المفاتيح المحفوظة.

لماذا كان العدد 3 وليس 2؟ لأن المفتاح username من المثال السابق ما زال محفوظاً. وهذه هي الفكرة كلها: البيانات لا تختفي حتى تحذفها أنت.

هذه أهم قاعدة في الدرس: localStorage يخزّن نصوصاً فقط. أي قيمة تعطيها له يحوّلها إلى نص أولاً.

localStorage.setItem("score", 50);
const score = localStorage.getItem("score");
console.log(typeof score);
console.log(score + 1);
console.log(Number(score) + 1);
الناتج
string
501
51

حفظنا الرقم 50 فعاد النص "50"، وعندما جمعنا عليه 1 التصق الرقمان. الحل أن تحوّل القيمة بـ Number بعد قراءتها.

حفظ الكائنات والمصفوفات

Section titled “حفظ الكائنات والمصفوفات”

وماذا لو حفظت كائناً مباشرة؟

const settings = { theme: "dark", fontSize: 18 };
localStorage.setItem("settings", settings);
console.log(localStorage.getItem("settings"));
الناتج
[object Object]

ضاعت البيانات! تحويل الكائن إلى نص بالطريقة العادية يعطي هذه العبارة عديمة الفائدة. والحل تعرفه من درس JSON: حوّل الكائن بـ JSON.stringify قبل الحفظ، وأعده بـ JSON.parse بعد القراءة.

localStorage.setItem("settings", JSON.stringify(settings));
const savedSettings = JSON.parse(localStorage.getItem("settings"));
console.log(savedSettings.theme);
console.log(savedSettings.fontSize + 2);
الناتج
dark
20

عاد الكائن كاملاً، والرقم عاد رقماً.

نمط جاهز: دالة تحمّل ودالة تحفظ

Section titled “نمط جاهز: دالة تحمّل ودالة تحفظ”

في المشاريع نجمع التعامل مع المخزن في دالتين، ونستخدمهما في كل مكان:

function loadTasks() {
const text = localStorage.getItem("tasks");
if (text === null) {
return [];
}
return JSON.parse(text);
}
function saveTasks(tasks) {
localStorage.setItem("tasks", JSON.stringify(tasks));
}
const tasks = loadTasks();
console.log(tasks.length);
tasks.push("مذاكرة الدرس");
saveTasks(tasks);
console.log(loadTasks());
الناتج
0
[ 'مذاكرة الدرس' ]

انتبه إلى الشرط text === null: في أول زيارة لا يوجد شيء محفوظ، فنُرجع مصفوفة فارغة ليعمل باقي الكود بلا مشاكل. نسيان هذه الحالة من أشهر أخطاء المبتدئين.

مثال عملي: عدّاد الزيارات

Section titled “مثال عملي: عدّاد الزيارات”
<p id="visits"></p>
<script>
const count = Number(localStorage.getItem("visits")) + 1;
localStorage.setItem("visits", count);
document.querySelector("#visits").textContent = `زرت هذه الصفحة ${count} مرة`;
</script>

في أول زيارة تُرجع getItem القيمة null، و Number(null) تساوي 0، فيصبح العدد 1. ومع كل تحديث للصفحة يزيد العدد واحداً، حتى لو أغلقت المتصفح وعدت في الغد.

أين ترى البيانات المحفوظة؟

Section titled “أين ترى البيانات المحفوظة؟”

افتح أدوات المطوّر بـ F12، ثم:

  • في Chrome و Edge: تبويب Application، ثم Local Storage من القائمة الجانبية.
  • في Firefox: تبويب Storage، ثم Local Storage.

سترى جدولاً بكل المفاتيح والقيم، وتستطيع تعديلها وحذفها بيدك. وهذا مفيد جداً عند البحث عن خطأ.

للمتصفح مخزن آخر اسمه sessionStorage، وله الدوال نفسها تماماً. الفرق الوحيد في العمر:

المخزن متى تُمسح البيانات؟
localStorage لا تُمسح حتى تحذفها أنت أو المستخدم
sessionStorage عند إغلاق التبويب

استخدم sessionStorage لما تحتاجه خلال الزيارة الحالية فقط، مثل الخطوة التي وصل إليها المستخدم في نموذج طويل.

  • المساحة صغيرة: حوالي 5 ميجابايت للموقع. تكفي للإعدادات والقوائم، ولا تصلح للصور والملفات.
  • على هذا الجهاز فقط: ما تحفظه في متصفح الحاسوب لا يظهر على هاتفك.
  • المستخدم قد يمسحه: إذا مسح بيانات التصفح ضاع المخزن، فلا تجعله المكان الوحيد لبيانات مهمة.

أنشئ ملفاً باسم theme.html، واكتب فيه صفحة تتذكر هل اختار المستخدم الوضع الليلي:

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>الوضع الليلي</title>
<style>
body.dark { background: #171b2e; color: #eef0f8; }
</style>
</head>
<body>
<h1>مرحباً بك</h1>
<button id="toggle">بدّل الوضع</button>
<script>
if (localStorage.getItem("theme") === "dark") {
document.body.classList.add("dark");
}
document.querySelector("#toggle").addEventListener("click", () => {
const isDark = document.body.classList.toggle("dark");
localStorage.setItem("theme", isDark ? "dark" : "light");
});
</script>
</body>
</html>

الدالة classList.toggle تُرجع true إذا أضافت الكلاس و false إذا أزالته، فنعرف منها الوضع الحالي ونحفظه.

افتح الملف واضغط الزر ليصبح الوضع ليلياً، ثم حدّث الصفحة: يجب أن تبقى ليلية. بعد ذلك أضف عدّاداً يعرض كم مرة بدّل المستخدم الوضع، واحفظه هو أيضاً.

  • حفظ كائن أو مصفوفة بدون JSON.stringify: تُحفظ العبارة [object Object] وتضيع البيانات.
  • نسيان أن القيمة نص: getItem("score") + 1 تلصق النصين. حوّل بـ Number أو بـ JSON.parse أولاً.
  • تجاهل حالة null: في أول زيارة لا توجد بيانات، فاستدعاء .length أو .push على null يوقف السكربت بخطأ.

سؤال

ماذا يطبع الكود التالي؟ localStorage.setItem('n', 7); console.log(localStorage.getItem('n') + 3);

سؤال

ما الطريقة الصحيحة لحفظ مصفوفة اسمها tasks؟

  • localStorage يحفظ بيانات صغيرة في متصفح المستخدم، وتبقى بعد التحديث والإغلاق.
  • setItem للحفظ، و getItem للقراءة، و removeItem و clear للحذف.
  • كل القيم تُحفظ نصوصاً: استخدم JSON.stringify عند الحفظ و JSON.parse عند القراءة.
  • getItem تُرجع null في أول زيارة، فجهّز قيمة افتراضية.
  • لا تحفظ فيه كلمات مرور ولا أي بيانات حساسة.