يسمح هذان الكائنان بتخزين الأزواج "مفتاح/قيمة" في المتصفح، لكن الميزة الهامة لهما هي بقاء البيانات المخزنة في الكائن sessionStorage
بعد تحديث الصفحة وبقاء المعلومات المخزنة في localStorage
بعد إعادة تشغيل المتصفح، لكن السؤال الذي يلفت النظر هو: ما دام لدينا ملفات تعريف الارتباط cookies، فلماذا سنستخدم كائنات إضافيةً؟ والجواب:
- لا يُرسَل كائنا تخزين بيانات الويب هذان إلى الخادم مع كل طلب، وذلك خلافًا لملفات تعريف الارتباط، وبالتالي سنتمكن من تخزين بيانات أكثر، حيث تتيح أغلب المتصفحات حوالي 2 ميغابايت من البيانات -وأكثر-، ولها إعدادات لتهيئة حجم التخزين.
- لا يمكن للخادم التحكم بهذين الكائنين عبر ترويسات HTTP، وسيُنجز كل شيء باستخدام JavaScript، خلافًا لملفات تعريف الارتباط.
- ترتبط ذاكرة التخزين بالأصل الذي ولّدها (نطاق/بروتوكول/منفذ)، مما يعني أن البروتوكولات أو النطاقات الفرعية المختلفة ستدل على كائنات تخزين مختلفة، ولا يمكن أن تصل إلى بيانات بعضها البعض.
لكائني التخزين التوابع والخصائص نفسها، وهي:
-
(setItem(key, value
: يخزّن الأزواج "مفتاح/قيمة". -
(getItem(key
: يحصل على القيمة عن طريق المفتاح. -
(removeItem(key
: يزيل المفتاح مع قيمته. -
()clear
: يحذف كل شيء. -
(key(index
: يعيد مفتاحًا ذا موقع محدد. -
length
: يعطي عدد العناصر المخزّنة.
تشبه هذه التوابع ما يقوم به الترابط Map، لكنه يسمح أيضًا بالوصول إلى المفاتيح من خلال مواقعها (key(index
.
مثال نموذجي عن الكائن localStorage
ميزات هذا الكائن الرئيسية هي:
- مشترك بين كل النوافذ التي تشترك بالأصل ذاته.
- ليس للبيانات فترة صلاحية، إذ تبقى بعد إعادة تشغيل المتصفح أو نظام التشغيل.
فلو شغّلنا الشيفرة التالية مثلًا:
localStorage.setItem('test', 1);
إذا أغلقنا المتصفح ثم أعدنا تشغيله، أو فتحنا الصفحة نفسها في أكثر من نافذة، فسنحصل على القيمة التي خزناها بالشكل التالي:
alert( localStorage.getItem('test') ); // 1
علينا فقط أن نكون ضمن صفحات تنتمي إلى الأصل ذاته (نطاق/منفذ/بروتوكول)، على الرغم من إمكانية اختلاف المسار، لأن الكائن localStorage
مشترك بين كل النوافذ التي تنتمي إلى الأصل ذاته.
الوصول بأسلوب الكائنات
يمكن استخدام أسلوب الكائن البسيط للحصول على المفاتيح أو تغيير قيمها بالشكل التالي:
// ضبط المفتاح localStorage.test = 2; // الحصول على قيمة المفتاح alert( localStorage.test ); // 2 // إزالة المفتاح delete localStorage.test;
يُسمح بهذه الطريقة -التي ستعمل غالبًا- لأسباب تاريخية، لكن لا يُفضّل استعمالها للأسباب التالية:
-
لو ولّد المستخدم المفتاح، فقد يكون أي شيء مثل
length
أوtoString
، أي قد يتشابه مع توابع محجوزة تستخدَم معlocalStorage
، في هذه الحالة ستعمل التوابعgetItem/setItem
، لكن سيخفق الوصول بأسلوب الكائنات .
let key = 'length'; localStorage[key] = 5; // Error, can't assign length
-
الحدث
storage
الذي يقع عند تعديل البيانات، وليس عند تطبيق أسلوب الكائنات، وسنرى ذلك لاحقًا في هذا المقال.
التنقل بين المفاتيح ضمن حلقات
تؤمّن التوابع السابقة وظائف الحصول على قيم المفاتيح وضبطها وحذفها، لكن كيف سنخزّن جميع القيم أو المفاتيح؟ لسوء الحظ لا تقبل كائنات تخزين البيانات التكرار، لكن إحدى الطرق المتبعة هي التنقل في حلقة كما لو أننا نتعامل مع مصفوفة:
for(let i=0; i<localStorage.length; i++) { let key = localStorage.key(i); alert(`${key}: ${localStorage.getItem(key)}`); }
يمكن استخدام الحلقة for key in localStorage
كما نفعل مع الكائنات النظامية، حيث تتكرر تعليمات الحلقة وفقًا للمفاتيح المخزّنة، لكنها ستعطي حقولًا مدمجةً غير مطلوبة:
// محاولة فاشلة for(let key in localStorage) { alert(key); // وغيرها من الحقول المدمجة getItem, setItem ستظهر }
فإمّا أن نرشح الحقول التي ستُعرض بالتحقق من الخاصية hasOwnProperty
:
for(let key in localStorage) { if (!localStorage.hasOwnProperty(key)) { continue; // "setItem", "getItem" تتجاهل مفاتيح مثل } alert(`${key}: ${localStorage.getItem(key)}`); }
أو نحصل على المفاتيح الخاصة بالكائن باستخدام الأمر Object.keys
، ثم نطبق الحلقة عليها:
let keys = Object.keys(localStorage); for(let key of keys) { alert(`${key}: ${localStorage.getItem(key)}`); }
ستنجح الطريقة الأخيرة لأنّ التابع Object.keys
سيعيد المفاتيح التي ترتبط بالكائن فقط، ويتجاهل النموذج الأولي prototype.
قيم نصية فقط
يجب الانتباه إلى أنّ المفاتيح وقيمها كائنات نصية، وستحوّل أي أنواع أخرى - مثل الأرقام- إلى قيم نصية تلقائيًا:
sessionStorage.user = {name: "John"}; alert(sessionStorage.user); // [object Object]
يمكن استخدام JSON لتخزين الكائنات أيضًا:
sessionStorage.user = JSON.stringify({name: "John"}); // لاحقًا let user = JSON.parse( sessionStorage.user ); alert( user.name ); // John
كما يمكن تحويل كائن التخزين بالكامل إلى نص، لأغراض التنقيح مثلًا:
// لتبدو النتيجة أفضل JSON.stringify يمكن إضافة خيارات تنسيق إلى alert( JSON.stringify(localStorage, null, 2) );
الكائن sessionStorage
يُستخدم في حالات أقل من الكائن localStorage، وله نفس التوابع والخصائص، لكنها أكثر محدوديةً.
- يتواجد الكائن فقط ضمن النافذة الحالية المفتوحة ضمن المتصفح.
- سيكون لنافذة أخرى مفتوحة ضمن المتصفح كائن آخر خاص بها.
- تتشارك النوافذ الضمنية الموجودة في نافذة نفس الكائن، بفرض أنها مشتركة بالأصل.
- تبقى البيانات المخزنة بعد تحديث الصفحة، لكنها تُحذف عند إغلاق النافذة وإعادة فتحها.
شغّل هذه الشيفرة لترى آلية عمل الكائن:
sessionStorage.setItem('test', 1);
ثم حدّث الصفحة. عندها ستلاحظ أن البيانات مازالت موجودة:
alert( sessionStorage.getItem('test') ); // after refresh: 1
لكن لو فتحت الصفحة نفسها في نافذة أخرى، وحاولت تنفيذ الشيفرة السابقة مجددًا، فستعيد القيمة "null" أي أنها لم تجد شيئًا، لأنّ الكائن sessionStorage
لا يتعلق فقط بالأصل المشترك بل بالنافذة المفتوحة في المتصفح، لذا يندر استخدامه.
أحداث التخزين
عندما تُحدّث البيانات ضمن كائني التخزين فستقع أحداث التخزين التالية:
-
key
: المفتاح الذي تغيّر، وسيعيدnull
إذا استدعي التابع()clear
. -
oldValue
: القيمة القديمة، وستكونnull
إذا أضيف المفتاح حديثًا. -
newValue
: القيمة الجديدة، وستكونnull
إذا حُذف المفتاح. -
url
: عنوان الصفحة التي حدث فيها التغيير. -
storageArea
أو أحد الكائنينlocalStorage
أوsessionStorage
حيث حدث التغيير.
أمّا الأمر الهام فهو أنّ هذه الأحداث ستقع في كل الكائنات window
التي يمكن فيها الوصول إلى كائن تخزين البيانات، عدا تلك التي سببت وقوع الحدث.
تخيل وجود نافذتين في المتصفح تعرضان الصفحة نفسها، عندئذ ستتشارك النافذتان الكائن localStorage
نفسه، وقد يكون عرض الصفحة في نافذتين مختلفتين مناسبًا لاختبار الشيفرة التي سنعرضها تاليًا، إذا استمعت كلتا النافذتين إلى الحدث window.onstorage
، فستتفاعلان مع التحديثات التي تجري في كلٍّ منهما.
// يقع عند تحديث كائن التخزين من قبل صفحة أخرى window.onstorage = event => { // same as window.addEventListener('storage', event => { if (event.key != 'now') return; alert(event.key + ':' + event.newValue + " at " + event.url); }; localStorage.setItem('now', Date.now());
لاحظ أنّ الحدث سيتضمن أيضًا event.url
، وهو عنوان الصفحة التي حصل فيها التغيير، كما يتضمن الحدث كائن التخزين (سيبقى الحدث نفسه للكائنين sessionStorage
وlocalStorage
)، لذا سيشير الحدث event.storageArea
إلى الكائن الذي جرى تعديله منهما، وبما أننا نريد أن نعيد ضبط قيمة ما استجابةً للتغيير، فسيسمح ذلك للنوافذ ذات الأصل المشترك بتبادل الرسائل.
تدعم المتصفحات الحديثة الواجهة البرمجية لقناة البث Broadcast channel API، وهي واجهة خاصة بتبادل الرسائل بين النوافذ التي لها أصل مشترك. لهذه الواجهة ميزات متكاملة أكثر لكنها أقل دعمًا، ومع ذلك فستجد الكثير من المكتبات التي توائم هذه الواجهة مع المتصفحات بالاستفادة من الكائن localStorage
مما يجعلها متاحةً في أي مكان.
خلاصة
يسمح الكائنان localStorage
وsessionStorage
بتخزين الأزواج (مفتاح/قيمة) في المتصفح.
-
المفتاح
key
والقيمةvalue
من النوع النصي. - الحد الأقصى للتخزين بحدود 5 ميغابايت وذلك تبعًا للمتصفح.
- ليس لها فترة صلاحية.
- ترتبط البيانات بأصل الصفحة (نطاق/ منفذ/بروتوكول).
localStorage
|
sessionStorage
|
---|---|
مشترك بين النوافذ التي لها نفس الأصل | تُرى ضمن نافذة واحدة في المتصفح بما فيها النوافذ الضمنية التي لها نفس الأصل |
تبقى بعد إعادة تشغيل المتصفح | تبقى بعد تحديث الصفحة لكنها تحذف عند إغلاق النافذة |
الواجهة البرمجية:
-
(setItem(key, value
: يخزّن أزواج (مفتاح/قيمة). -
(getItem(key
: يحصل على القيمة عن طريق المفتاح. -
(removeItem(key
: يزيل المفتاح مع قيمته. -
()clear
: يحذف كل شيء. -
(key(index
: يعيد مفتاحًا ذا موقع محدد. -
length
: يعطي عدد العناصر المخزّنة. -
Object.keys
: للحصول على جميع المفاتيح. -
يمكن الوصول إلى المفاتيح عبر خصائص الكائن، لكن لن يقع الحدث
storage
في هذه الحالة.
أحداث التخزين:
-
تقع نتيجةً للاستدعاءات التالية
setItem
أوremoveItem
أوclear
. -
تحتوي على كل البيانات المتعلقة بالعملية
key/oldValue/newValue
، وبالصفحةurl
، وبكائن التخزينstorageArea
. -
تقع في جميع النوافذ التي يمكنها الوصول إلى كائن التخزين، عدا تلك التي ولّدته، ضمن نافذة واحدة بالنسبة للكائن
sessionStorage
، ولكل النوافذ بالنسبة للكائنlocalStorage
.
مهمات لإنجازها
الحفظ التلقائي لحقل من حقول نموذج
أنشئ حقلًا نصيًا textarea
يحفظ تلقائيًا القيمة التي يحتويها بعد كل تغيير فيها، وبالتالي عندما يُغلق المستخدم الصفحة عن طريق الخطأ، ثم يفتحها مجددًا، فسيجد النص الذي لم يكمله بعد في مكانه.
افتح المثال في بيئة تجريبية. وإن أردت الحل، فهو في هذه البيئة التجريبية.
ترجمة -وبتصرف- للفصل localStorage, sessionStorage من سلسلة The Modern JavaScript Tutorial.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.