JSON.stringify
هي طريقة تستخدم غالبًا في التطوير اليومي، هل يمكنك حقًا استخدامها بمرونة؟
قبل دراسة هذه المقالة، يريد Xiaobao من الجميع الإجابة على بعض الأسئلة والتعلم stringify
.
stringify
على عدة معلمات، ما هو استخدام كل معلمة؟stringify
؟ null、undefined、NaN
؟ES6
هل ستكون هناك معاملة خاصة أثناء عملية التسلسل لنوع Symbol
و BigInt
؟stringify
مناسبًا للنسخ العميق؟stringify
؟سياق المقالة بأكملها متوافق مع الخريطة الذهنية أدناه يمكن أن تترك انطباعًا أولاً.
في البرمجة اليومية، غالبًا ما نستخدم طريقة JSON.stringify
لتحويل كائن إلى نموذج سلسلة JSON
.
كونست ستو = { الاسم: "زكشياوباو"، العمر: 18 } // {"الاسم": "zcxiaobao"، "العمر": 18} console.log(JSON.stringify(stu));
ولكن هل stringify
بهذه البساطة حقًا؟ دعونا أولاً نلقي نظرة على تعريف stringify
في MDN
.
تنص MDN: تقوم طريقة JSON.stringify()
بتحويل كائن JavaScript
أو قيمة إلى سلسلة JSON
إذا تم تحديد دالة replacer
، فيمكن استبدال القيمة اختياريًا، أو يكون replacer
المحدد عبارة عن مصفوفة .
بعد قراءة التعريف، تفاجأ Xiaobao هل لدى stringfy
أكثر من معلمة واحدة؟ وبطبيعة الحال، stringify
لديه ثلاث معلمات.
دعونا نلقي نظرة على بناء جملة stringify
ومقدمة المعلمة:
JSON.stringify(value[,replacer[,space]])
value
: القيمة التي سيتم تسلسلها في سلسلة JSON.replacer
(اختياري) إذا كانت المعلمة وظيفة ، أثناء عملية التسلسل، سيتم تحويل كل سمة من القيمة المتسلسلة ومعالجتها بواسطة الوظيفة؛
إذا كانت المعلمة مصفوفة ، فستكون الخصائص الموجودة في هذه المصفوفة هي السمة فقط سيتم إجراء تسلسل للأسماء الموجودة في سلسلة JSON
النهائية.
إذا كانت هذه المعلمة null
أو لم يتم توفيرها، فسيتم إجراء تسلسل لجميع سمات الكائن.
space
(اختياري): تحدد سلسلة المسافة البيضاء المستخدمة لتجميل المخرجات. إذا كانت المعلمة رقمًا، فإنها تمثل عدد المسافات. الحد الأعلى هو 10.
إذا كانت القيمة أقل من 1، فهذا يعني أنه لا توجد مسافات.
إذا كانت المعلمة عبارة عن سلسلة (عندما يتجاوز طول السلسلة 10 أحرف، يتم أخذ الأحرف العشرة الأولى)، فسيتم التعامل مع السلسلة
كمسافات لم يتم توفير المعلمة (أو كانت فارغة)، ولن يكون هناك
replacer
replacer
كدالة
replacer
له معلمتان، المفتاح ( key
) والقيمة ( value
)، وسيتم إجراء تسلسل لكلا المعلمتين.
في البداية، سيتم تمرير وظيفة الاستبدال في سلسلة فارغة كقيمة أساسية، تمثل الكائن المراد تقييده . من المهم أن نفهم ذلك، لا تقوم وظيفة replacer
بتحليل الكائن إلى أزواج ذات قيمة مفتاحية عند ظهورها، ولكنها تقوم أولاً بتمرير الكائن ليتم إجراء تسلسل له . ثم يتم تمرير الخصائص الموجودة على كل كائن أو صفيف بشكل تسلسلي. إذا كانت قيمة إرجاع الدالة غير محددة أو كانت الدالة، فسيتم تصفية قيمة السمة ، وسيتبع الباقي قواعد الإرجاع.
// يقبل repalcer قيمة مفتاح معلمتين // القيمة الرئيسية هي كل زوج من القيمة الرئيسية للكائن // حتى نتمكن ببساطة من التصفية بناءً على نوع المفتاح أو وظيفة القيمة البديلة (مفتاح، قيمة) { إذا (نوع القيمة === "سلسلة") { عودة غير محددة؛ } قيمة الإرجاع؛ } // يمكن للوظيفة اختبار الوظيفة بنفسها استبدالFunc(key, value) { إذا (نوع القيمة === "سلسلة") { العودة () => {}؛ } قيمة الإرجاع؛ } const foo = {المؤسسة: "Mozilla"، الطراز: "box"، الأسبوع: 45، النقل: "car"، الشهر: 7}; const jsonString = JSON.stringify(foo, Replacer);
نتيجة تسلسل JSON
هي {"week":45,"month":7}
ولكن إذا كان التسلسل مصفوفة، وإذا أعادت الدالة replacer
دالة أو undefined
محددة، فإن القيمة الحالية لن يتم تجاهله وسيتم استبداله بـ null
.
قائمة ثابتة = [1، '22'، 3] const jsonString = JSON.stringify(list, Replacer)
نتيجة تسلسل JSON
هي أن
replacer
أسهل في الفهم كمصفوفة، حيث يقوم بتصفية القيم الأساسية التي تظهر في المصفوفة.
const foo = {المؤسسة: "Mozilla"، الطراز: "box"، الأسبوع: 45، النقل: "car"، الشهر: 7}; const jsonString = JSON.stringify(foo, ['week', 'month']);
نتيجة تسلسل JSON هي {"week":45,"month":7}
، وتكون قيم سمات week
month
فقط احتفظ بها.
في قيم سمات الكائن غير المصفوفة: سيتم تجاهل قيم undefined
وأي دالة وقيم Symbol
أثناء عملية التسلسل
التي تظهر في المصفوفات: undefined
وأي دالة
وسيتم تجاهل قيم Symbol
عند تحويلها إلى قيمة خالية
فقط: سيتم إرجاع غير محدد
// 1. سيتم تجاهل وجود هذه القيم الثلاث في قيمة سمة الكائن const obj = { الاسم: "زك"، العمر: 18, // سيتم تجاهل الوظيفة sayHello() { console.log ("مرحبا بالعالم") }, // سيتم تجاهل الزوجة غير المحددة: غير محددة، // سيتم تجاهل قيمة الرمز المعرف: الرمز (111)، // [الرمز ('zc')]: 'zc'، } // نتيجة الإخراج: {"name": "zc"، "age": 18} console.log(JSON.stringify(obj)); // 2. سيتم تحويل هذه القيم الثلاث في المصفوفة إلى قيمة خالية قائمة ثابتة = [ "زك"، 18, // تم تحويل الدالة إلى قيمة فارغة وظيفة قل مرحبا () { console.log ("مرحبا بالعالم") }, // غير محدد تم تحويله إلى قيمة فارغة غير محدد, // تم تحويل الرمز إلى قيمة فارغة الرمز(111) ] // ["zc"،18،خالية،خالية،خالية] console.log(JSON.stringify(list)) // 3. سيؤدي التحويل المنفصل لهذه القيم الثلاث إلى إرجاع غير محدد console.log(JSON.stringify(undef)) // غير محدد console.log(JSON.stringify(Symbol(111))) // غير محدد console.log(JSON.stringify(function sayHello() { console.log ("مرحبا بالعالم") })) //
بتحويل القيمة إذا كانت هناك طريقة toJSON()
، فإن القيمة التي ترجعها طريقة toJSON()
ستكون هي القيمة التي ترجعها نتيجة التسلسل، وستكون القيم الأخرى. تم تجاهله.
كائن ثابت = { الاسم: "زك"، toJSON(){ العودة "العودة إلىJSON" } } // العودة إلى JSON console.log(JSON.stringify(obj));
سيتم تحويل كائنات التعبئة للقيم والأرقام والسلاسل المنطقية تلقائيًا إلى القيمة الأصلية المقابلة لـ JSON أثناء عملية التسلسل.
stringify([new Number(1), new String("zcxiaobao"), new Boolean(true)]); // [1،"zcxiaobao"، true]
الميزة الرابعة تستهدف بشكل أساسي القيم الخاصة في JavaScript
، مثل NaN
و Infinity
وnull في نوع Number
. سيتم التعامل مع هذه الأنواع الثلاثة من القيم على أنها null
أثناء التسلسل .
// [فارغة، فارغة، فارغة، فارغة، فارغة] JSON.stringify([خالية، NaN، -NaN، إنفينيتي، -Infinity]) // الميزة 3 ذكرت أن كائنات التعبئة للقيم والأرقام والسلاسل المنطقية سيتم تحويلها تلقائيًا إلى القيم الأصلية المقابلة أثناء عملية التسلسل // سوف يستدعي تحويل النوع الضمني فئة التغليف، لذلك سيكون Number => NaN دعا أولا // ثم قم بالتحويل إلى فارغة // 1/0 => إنفينيتي => خالية JSON.stringify([Number('123a'), +'123a', 1/0])
يتم نشر طريقة toJSON
(مثل Date.toISOString()
) على كائن Date
لتحويله إلى كائن سلسلة، لذلك سيقوم JSON.stringify() بإجراء تسلسل لقيمة التاريخ في سلسلة تنسيق الوقت .
// "2022-03-06T08:24:56.138Z" JSON.stringify(new Date())
عند ذكر ميزة الرمز، عند استخدام نوع Symbol
كقيمة، سيتم تجاهل الكائنات والمصفوفات والاستخدامات الفردية وتحويلها إلى null
وتحويلها إلى undefined
على التوالي.
وبالمثل، سيتم تجاهل جميع الخصائص التي تحتوي على الرمز كمفتاح الخاصية تمامًا، حتى لو تم تضمينها في المعلمة البديلة .
كائن ثابت = { الاسم: "زكشياوباو"، العمر: 18, [الرمز('ليل')]: 'فريد' } بديل الوظيفة (المفتاح، القيمة) { إذا (نوع المفتاح === 'رمز') { قيمة الإرجاع؛ } } // غير محدد JSON.stringify(obj, Replacer);
من الحالة المذكورة أعلاه، يمكننا أن نرى أنه على الرغم من أننا نحدد قيمة نوع Symbol
المرجع بقوة من خلال replacer
، إلا أنه سيتم تجاهله في النهاية.
JSON.stringify
على أن محاولة تحويل قيمة من النوع BigInt
ستؤدي إلى ظهور TypeError
const bigNumber = BigInt(1) // خطأ TypeError لم يتم اكتشافه: لا أعرف كيفية إجراء تسلسل لـ BigInt Console.log(JSON.stringify(bigNumber))
ميزة المرجع الدائري 8 تشير إلى: تنفيذ هذه الطريقة على الكائنات التي تحتوي على مراجع دائرية (تشير الكائنات إلى بعضها البعض، وتشكل حلقة لا نهائية) سيؤدي إلى حدوث خطأ
في النسخة العميقة للتطوير اليومي الطريقة الأبسط والأكثر عنفًا هي استخدام JSON.parse(JSON.stringify(obj))
ولكن النسخة العميقة ضمن هذه الطريقة لها مخاطر كبيرة. المشكلة الرئيسية هي أن stringify
لا يمكنه التعامل مع مشكلة المرجع الدائري.
كائن ثابت = { الاسم: "زكشياوباو"، العمر: 18, } حلقة ثابتةObj = { obj } // قم بتكوين مرجع دائري obj.loopObj = loopObj; JSON.stringify(obj) /* خطأ في الكتابة لم يتم اكتشافه: تحويل البنية الدائرية إلى JSON -> البدء من الكائن باستخدام المُنشئ "Object" |. الخاصية 'loopObj' -> كائن ذو مُنشئ 'Object' --- الخاصية 'obj' تغلق الدائرة في JSON.stringify (<مجهول>) في <مجهول>:10:6 */
لتسلسل الخصائص القابلة للإحصاء للكائنات (بما في ذلك Map/Set/WeakMap/WeakSet
)، بالإضافة إلى بعض المواقف المذكورة أعلاه، تنص stringify
أيضًا بوضوح على أنه سيتم إجراء تسلسل للخصائص القابلة للإحصاء فقط
// Non-enumerable سيتم تجاهل الخصائص بشكل افتراضي // {"age":18} JSON.stringify( كائن.إنشاء( باطل، { الاسم: {القيمة: 'zcxiaobao'، قابلة للتعداد: خطأ }، العمر: { القيمة: 18، قابلة للتعداد: صحيح } } )
;
localStorage
عادة نقوم بتخزينه على شكل أشياء.
ما عليك سوى استدعاء أسلوب كائن localStorage
const obj = { الاسم: "زكشياوباو"، العمر: 18 } // ما عليك سوى الاتصال بـ localStorage.setItem() localStorage.setItem('zc', obj); // نتيجة الإرجاع النهائية هي [كائن كائن] // يمكن ملاحظة أن استدعاء localStorage يفشل ببساطة console.log(localStorage.getItem('zc'))
يتعاون localStorage
مع طريقة JSON.stringify
localStorage.setItem('zc', JSON.stringify(obj)); // نتيجة الإرجاع النهائية هي {name: 'zcxiaobao', age: 18} Console.log(JSON.parse(localStorage.getItem('zc')))تفترض
مثل هذا السيناريو. تقوم الواجهة الخلفية بإرجاع كائن طويل يحتوي على العديد من السمات، ونحتاج فقط إلى عدد قليل منها ونحتاج إلى تخزينها السمات في localStorage
الخيار 1: مهمة التدمير + stringify
// نحتاج فقط إلى السمات a وe وf const obj = { أ:1، ب:2، ج:3، د:4، ه:5، و:6، ز:7 } // تدمير المهمة const {a,e,f} = obj; // قم بالتخزين في localStorage localStorage.setItem('zc', JSON.stringify({a,e,f})) // {"أ":1،"e":5،"f":6} console.log(localStorage.getItem('zc'))
يستخدم المعلمة replacer
لـ stringify
// استخدم البديل للتصفية كمصفوفة localStorage.setItem('zc', JSON.stringify(obj, ['a','e' ، 'و'])) // {"أ":1،"e":5،"f":6} console.log(localStorage.getItem('zc'))
عندما يكون replacer
عبارة عن مصفوفة، يمكننا ببساطة تصفية السمات التي نحتاجها، وهي خدعة صغيرة جيدة.
يعد استخدام JSON.parse(JSON.stringify)
أحد أبسط الطرق وأكثرها عنفًا لتنفيذ نسخ عميقة من الكائنات. ولكن كما يقول العنوان، فإن استخدام طريقة النسخ العميق هذه يتطلب دراسة متأنية.
مشكلة المرجع الدائري، ستبلغ stringify
عن
وظيفة خطأ، undefined
، سيتم تجاهل Symbol
،
وسيتم إجراء تسلسل NaN
و Infinity
و -Infinity
إلى null
...
لذلك، عند استخدام JSON.parse(JSON.stringify)
لإجراء نسخة عميقة، يجب عليك فكر بعناية. إذا لم تكن هناك مخاطر مخفية مذكورة أعلاه، فإن JSON.parse(JSON.stringify)
هو حل ممكن للنسخ العميق.
عند البرمجة باستخدام المصفوفات، غالبًا ما نستخدم وظيفة map
. باستخدام المعلمة replacer
، يمكننا استخدام هذه المعلمة لتنفيذ وظيفة map
للكائن.
const ObjectMap = (obj, fn) => { إذا (نوع الجبهة الوطنية! == "وظيفة") { طرح TypeError جديد(`${fn} ليس دالة!`); } // اتصل أولاً بـ JSON.stringify(obj, Replacer) لتنفيذ وظيفة الخريطة // ثم اتصل بـ JSON.parse لإعادة تحويله إلى كائن return JSON.parse(JSON.stringify(obj, fn)); }; // على سبيل المثال، يقوم ما يلي بضرب قيمة السمة لكائن obj في 2 كائن ثابت = { ج: 1، ب: 2، ج: 3 } console.log(ObjectMap(obj, (key, val) => { إذا (قيمة نوع === "رقم") { قيمة الإرجاع * 2؛ } قيمة الإرجاع؛ }))
قد يتساءل العديد من الطلاب عن سبب الحاجة إلى حكم إضافي، أليس من الممكن return value * 2
فقط؟
كما هو مذكور أعلاه، يتم تمرير وظيفة replacer
أولاً في الكائن المراد إجراء تسلسل له، ويتم تجاهل الكائن * 2 => NaN => toJSON(NaN) => undef => ، ولن يكون هناك تحليل لاحق لزوج القيمة الرئيسية.
باستخدام وظيفة replacer
، يمكننا أيضًا حذف سمات معينة للكائن.
كائن ثابت = { الاسم: "زكشياوباو"، العمر: 18 } // {"العمر":18} JSON.stringify(obj, (key, val) => { // عندما تكون القيمة المرجعة غير محددة، سيتم تجاهل هذه الخاصية if (key === 'name') { عودة غير محددة؛ } عودة فال؛ })
يمكن لـ JSON.stringify
إجراء تسلسل للكائنات في سلاسل، حتى نتمكن من استخدام أساليب السلسلة لتنفيذ أحكام بسيطة لمساواة الكائنات.
// تحديد ما إذا كان المصفوفة تحتوي على كائن أسماء const = [ {الاسم:'zcxiaobao'}، {الاسم:'txtx'}، {الاسم:'ميمي'}، ]; const zcxiaobao = {name:'zcxiaobao'}; // حقيقي JSON.stringify(names).includes(JSON.stringify(zcxiaobao)) // تحديد ما إذا كانت الكائنات متساوية const d1 = {type: 'div'} const d2 = {النوع: 'div'} // حقيقي JSON.stringify(d1) === JSON.stringify(d2);
بمساعدة الأفكار المذكورة أعلاه، يمكننا أيضًا تحقيق إلغاء البيانات المكررة لكائنات المصفوفة.
ولكن بما أن نتائج تسلسل JSON.stringify
{x:1, y:1}
و {y:1, x:1}
مختلفة، فنحن بحاجة إلى معالجة الكائنات الموجودة في المصفوفة قبل البدء.
الطريقة الأولى: ترتيب مفاتيح كل كائن في المصفوفة بترتيب القاموس
arr.forEach(item => { const newItem = {}; Object.keys(item) // الحصول على قيمة مفتاح الكائن.sort() // قيمة المفتاحsorting.map(key => { // إنشاء كائن جديد newItem[key] = item[key]; }) // استخدم newItem لإجراء عملية إلغاء البيانات المكررة})
لكن الطريقة الأولى مرهقة بعض الشيء، حيث توفر JSON.stringify
معلمة تنسيق المصفوفة replacer
، والتي يمكنها تصفية المصفوفة.
الطريقة الثانية: استخدام وظيفة تنسيق الصفيف replacer
Unique(arr) { const keySet = new Set(); كونست فريدوبج = {} // استخراج كافة المفاتيح arr.forEach(item => { Object.keys(item).forEach(key => keySet.add(key)) }) استبدال const = [...keySet]; arr.forEach(العنصر => { // تتم تصفية جميع الكائنات وفقًا لاستبدال قيمة المفتاح المحدد Unique[JSON.stringify(item, Replacer)] = item; }) إرجاع Object.keys(unique).map(u => JSON.parse(u)) } //اختبار فريد([{}، {}، {س:1}، {س:1}، {أ:1}، {س:1،أ:1}، {س:1،أ:1}، {س:1،أ:1،ب:1} ]) // إرجاع النتيجة [{},{"x":1},{"a":1},{"x":1,"a":1},{"x":1,"a":1 "ب":1}]