JSON.stringify
เป็นวิธีที่มักใช้ในการพัฒนารายวัน คุณสามารถนำไปใช้ได้อย่างยืดหยุ่นจริงหรือ?
ก่อนที่จะศึกษาบทความนี้ Xiaobao ต้องการให้ทุกคนถามคำถามสองสามข้อและเรียนรู้ stringify
ในเชิงลึก
stringify
มีพารามิเตอร์หลายตัว แต่ละพารามิเตอร์ใช้อย่างไรstringify
การทำให้ null、undefined、NaN
จัดการอย่างไรES6
จะมีการดูแลเป็นพิเศษในระหว่างกระบวนการทำให้เป็นอนุกรมของประเภท Symbol
และ BigInt
หรือstringify
จึงไม่เหมาะสำหรับการคัดลอกแบบลึกstringify
ที่ยอดเยี่ยมเหล่านั้นหรือไม่บริบทของบทความทั้งหมดสอดคล้องกับแผนผังความคิดด้านล่าง สามารถทิ้งความประทับใจไว้ก่อนได้
ในการเขียนโปรแกรมรายวัน เรามักใช้เมธอด JSON.stringify
เพื่อแปลงอ็อบเจ็กต์ให้อยู่ในรูปแบบสตริง JSON
const สตู = { ชื่อ: 'zcxiaobao', อายุ: 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[, replacementr [, space]])
value
: ค่าที่จะเรียงลำดับเป็นสตริง JSONreplacer
(ทางเลือก) หากพารามิเตอร์เป็น ฟังก์ชัน ในระหว่างกระบวนการซีเรียลไลซ์ แต่ละแอตทริบิวต์ของค่าซีเรียลไลซ์จะถูกแปลงและประมวลผลโดยฟังก์ชัน
หากพารามิเตอร์เป็น อาร์เรย์ เฉพาะคุณสมบัติที่มีอยู่ในอาร์เรย์นี้เท่านั้นที่จะเป็นแอตทริบิวต์เท่านั้น ชื่อในจะถูกทำให้เป็นอนุกรมในสตริง JSON
สุดท้าย
หากพารามิเตอร์นี้เป็น null
หรือไม่ได้ระบุ คุณลักษณะทั้งหมดของออบเจ็กต์จะถูกทำให้เป็นอนุกรม
space
(เป็นทางเลือก): ระบุสตริงช่องว่างที่ใช้สำหรับการเยื้อง ใช้เพื่อตกแต่งเอาต์พุตให้สวยงาม หากพารามิเตอร์เป็นตัวเลข จะแสดงจำนวนช่องว่าง ขีดจำกัดบนคือ 10
หากค่าน้อยกว่า 1 แสดงว่าไม่มีการเว้นวรรค
ยาว
ของสตริงเกิน 10 ตัวอักษร จะใช้ตัวอักษร 10 ตัวแรก) สตริงจะถือเป็นช่องว่าง
ไม่ได้ระบุพารามิเตอร์ไว้ (หรือเป็นโมฆะ) จะไม่มี
เรามาลองใช้ replacer
replacer
เป็นฟังก์ชัน
replacer
เป็นฟังก์ชัน โดยมีพารามิเตอร์สองตัว คีย์ ( key
) และค่า ( value
) และพารามิเตอร์ทั้งสองจะถูกทำให้เป็นอนุกรม
ที่จุดเริ่มต้น ฟังก์ชันการแทนที่จะถูกส่งผ่านในสตริงว่างเป็นค่าคีย์ ซึ่งแสดงถึงอ็อบเจ็กต์ที่จะสตริง สิ่งสำคัญคือต้องเข้าใจสิ่งนี้ ฟังก์ชัน replacer
จะไม่แยกวิเคราะห์ออบเจ็กต์เป็นคู่คีย์-ค่าเมื่อปรากฏขึ้น แต่ก่อนอื่นจะส่งผ่านไปยัง ออบเจ็กต์ที่จะซีเรียลไลซ์ จากนั้นคุณสมบัติของแต่ละอ็อบเจ็กต์หรืออาเรย์จะถูกส่งผ่านตามลำดับ หากค่าที่ส่งคืนของฟังก์ชันไม่ได้กำหนดไว้หรือเป็นฟังก์ชัน ค่าแอตทริบิวต์จะถูกกรองออก และส่วนที่เหลือจะเป็นไปตามกฎการส่งคืน
// repalcer ยอมรับค่าคีย์พารามิเตอร์สองตัว // ค่าคีย์คือคู่คีย์-ค่าแต่ละคู่ของออบเจ็กต์ // ดังนั้นเราจึงสามารถกรองตามประเภทของคีย์หรือฟังก์ชันแทนที่ค่า (คีย์, ค่า) { ถ้า (ประเภทของค่า === "สตริง") { กลับไม่ได้กำหนด; - ค่าส่งคืน; - // ฟังก์ชั่นสามารถทดสอบฟังก์ชั่นได้ด้วยตัวเอง replacementrFunc(key, value) { ถ้า (ประเภทของค่า === "สตริง") { กลับ () => {}; - ค่าส่งคืน; - const foo = {มูลนิธิ: "Mozilla" รุ่น: "box" สัปดาห์: 45 การขนส่ง: "รถยนต์" เดือน: 7}; const jsonString = JSON.stringify(foo, replacementr);
ผลลัพธ์การทำให้เป็นอนุกรม JSON
คือ {"week":45,"month":7}
แต่หากการทำให้เป็นอนุกรมเป็นอาร์เรย์ หากฟังก์ชัน replacer
ส่งคืน undefined
หรือฟังก์ชัน ค่าปัจจุบัน จะไม่ถูกละเลยและจะถูกแทนที่ด้วย null
รายการ const = [1, '22', 3] const jsonString = JSON.stringify(list, replacementr)
ผลลัพธ์ของการทำให้เป็นอนุกรม JSON
คือ '[1,null,3]'
replacer
เข้าใจง่ายกว่าในฐานะอาร์เรย์กรองค่าคีย์ที่ปรากฏในอาร์เรย์
const foo = {มูลนิธิ: "Mozilla" รุ่น: "box" สัปดาห์: 45 การขนส่ง: "รถยนต์" เดือน: 7}; const jsonString = JSON.stringify(foo, ['week', 'month']);
ผลลัพธ์การทำให้เป็นอนุกรม JSON คือ {"week":45,"month":7}
และมีเพียงค่าแอตทริบิวต์ week
และ month
เท่านั้น เก็บไว้
ที่ปรากฏในค่าแอตทริบิวต์ของวัตถุที่ไม่ใช่อาร์เรย์: undefined
ฟังก์ชั่นใด ๆ และค่า Symbol
จะถูก ละเว้น ในระหว่างกระบวนการทำให้เป็นอนุกรม
ที่ปรากฏในอาร์เรย์: undefined
ฟังก์ชั่นใด ๆ และค่า Symbol
จะถูกละเว้น เมื่อแปลงเป็น null
เพียงอย่างเดียว: ไม่ได้กำหนด จะถูกส่งกลับ
// 1 การมีอยู่ของทั้งสามค่านี้ในค่าแอตทริบิวต์ของวัตถุจะถูกละเว้น const obj = { ชื่อ: 'zc', อายุ: 18, // ฟังก์ชั่นจะถูกละเว้น sayHello() { console.log('สวัสดีชาวโลก') - // ไม่ได้กำหนดจะถูกละเว้น ภรรยา: ไม่ได้กำหนด, // ค่าสัญลักษณ์จะถูกละเว้น id: สัญลักษณ์ (111) // [สัญลักษณ์('zc')]: 'zc', - // ผลลัพธ์ที่ได้: {"name": "zc", "age":18} console.log(JSON.stringify(obj)); // 2. ค่าทั้งสามนี้ในอาร์เรย์จะถูกแปลงเป็นโมฆะ รายการ const = [ 'ซีซี', 18, // ฟังก์ชั่นแปลงเป็นโมฆะ ฟังก์ชั่น sayHello() { console.log('สวัสดีชาวโลก') - // ไม่ได้กำหนด แปลงเป็นโมฆะ ไม่ได้กำหนด, // สัญลักษณ์ถูกแปลงเป็นโมฆะ สัญลักษณ์(111) - // ["zc",18,null,null,null] console.log (JSON.stringify (รายการ)) // 3. การแปลงค่าทั้งสามค่าแยกจากกันจะส่งกลับค่าที่ไม่ได้กำหนด console.log (JSON.stringify (ไม่ได้กำหนด)) // ไม่ได้กำหนด console.log(JSON.stringify(สัญลักษณ์(111))) // ไม่ได้กำหนด console.log (JSON.stringify (ฟังก์ชั่น sayHello () { console.log('สวัสดีชาวโลก') })) //
จะแปลงค่า หากมีวิธี toJSON()
ค่าใดก็ตามที่วิธีการ toJSON()
ส่งคืนจะเป็นค่าใดที่ผลลัพธ์การทำให้เป็นอนุกรมส่งคืน และค่าอื่น ๆ จะเป็น ละเลย
const obj = { ชื่อ: 'zc', toJSON(){ กลับ 'กลับไปที่ JSON' - - // กลับสู่ JSON console.log(JSON.stringify(obj));
ค่าบูลีน ตัวเลข และสตริง
. stringify([หมายเลขใหม่(1), สตริงใหม่("zcxiaobao"), บูลีนใหม่(จริง)]); // [1,"zcxiaobao",true]
คุณสมบัติที่สี่มุ่งเน้นไปที่ค่าพิเศษใน JavaScript
เป็นหลัก เช่น NaN
, Infinity
และ null ในประเภท Number
ค่าทั้งสามประเภทนี้ จะถือเป็น null
ระหว่างการทำให้เป็นอนุกรม
// [โมฆะ, โมฆะ, โมฆะ, โมฆะ, โมฆะ] JSON.stringify([null, NaN, -NaN, Infinity, -Infinity]) // คุณสมบัติ 3 กล่าวถึงว่าออบเจ็กต์บรรจุภัณฑ์ของค่าบูลีน ตัวเลข และสตริงจะถูกแปลงเป็นค่าดั้งเดิมที่เกี่ยวข้องโดยอัตโนมัติในระหว่างกระบวนการซีเรียลไลซ์ // การแปลงประเภทโดยนัยจะเรียกคลาสบรรจุภัณฑ์ ดังนั้น Number => NaN จะเป็น โทรมาก่อน // จากนั้นแปลงเป็นโมฆะ // 1/0 => อนันต์ => โมฆะ JSON.stringify([Number('123a'), +'123a', 1/0])
วิธี toJSON
(เหมือนกับ Date.toISOString()
) ถูกปรับใช้บนวัตถุ Date
เพื่อแปลงเป็น string ดังนั้น JSON.stringify() จะทำให้ค่า Date เป็นอนุกรมในรูปแบบเวลา string
// "2022-03-06T08:24:56.138Z" JSON.stringify(new Date())
เมื่อกล่าวถึงคุณลักษณะสัญลักษณ์ เมื่อใช้ประเภท Symbol
เป็นค่า ออบเจ็กต์ อาร์เรย์ และการใช้งานส่วนบุคคลจะถูกละเว้น แปลงเป็น null
และแปลงเป็น undefined
กำหนดตามลำดับ
ในทำนองเดียวกัน คุณสมบัติทั้งหมดที่มีสัญลักษณ์เป็นคีย์คุณสมบัติจะถูกละเว้นโดยสิ้นเชิง แม้ว่าจะถูกบังคับให้รวมไว้ในพารามิเตอร์แทนที่ ก็ตาม
const obj = { ชื่อ: 'zcxiaobao', อายุ: 18, [สัญลักษณ์('ลิล')]: 'ไม่ซ้ำใคร' - ตัวแทนที่ฟังก์ชัน (คีย์, ค่า) { ถ้า (คีย์ประเภท === 'สัญลักษณ์') { ค่าส่งคืน; - - // ไม่ได้กำหนด JSON.stringify(obj, replacementr);
จากกรณีข้างต้น เราจะเห็นว่าแม้ว่าเราจะระบุค่าประเภท return Symbol
ผ่านทางการ replacer
แต่สุดท้ายก็จะถูกละเว้น
JSON.stringify
กำหนด: การพยายามแปลงค่าประเภท BigInt
จะส่ง TypeError
const bigNumber = BigInt(1) // Uncaught TypeError: ไม่ทราบวิธีทำให้ BigInt เป็นอนุกรม Console.log(JSON.stringify(bigNumber))
คุณลักษณะการอ้างอิงแบบวงกลม 8 ชี้ให้เห็น: การดำเนินการวิธีนี้กับออบเจ็กต์ที่มีการอ้างอิงแบบวงกลม (ออบเจ็กต์อ้างอิงถึงกัน ซึ่งสร้างวงวนไม่สิ้นสุด) จะทำให้เกิดข้อผิดพลาด
Daily Development Deep Copy วิธีที่ง่ายที่สุดและรุนแรงที่สุดคือการใช้ JSON.parse(JSON.stringify(obj))
แต่การคัดลอกแบบลึกภายใต้วิธีนี้มีข้อผิดพลาดอย่างมาก ปัญหาสำคัญคือ stringify
ไม่สามารถจัดการปัญหาการอ้างอิงแบบวงกลมได้
const obj = { ชื่อ: 'zcxiaobao', อายุ: 18, - const loopObj = { วัตถุประสงค์ - // สร้างการอ้างอิงแบบวงกลม obj.loopObj = loopObj; JSON.stringify(obj) /* Uncaught TypeError: การแปลงโครงสร้างแบบวงกลมเป็น JSON -> เริ่มต้นที่วัตถุด้วยตัวสร้าง 'Object' |. คุณสมบัติ 'loopObj' -> วัตถุที่มีตัวสร้าง 'Object' --- คุณสมบัติ 'obj' ปิดวงกลม ที่ JSON.stringify (<ไม่ระบุชื่อ>) ที่ <ไม่ระบุชื่อ>:10:6 */
การทำให้เป็นอนุกรมของคุณสมบัติที่นับได้สำหรับวัตถุ (รวมถึง Map/Set/WeakMap/WeakSet
) นอกเหนือจากบางสถานการณ์ที่กล่าวถึงข้างต้น stringify
ยังกำหนดไว้อย่างชัดเจนว่า เฉพาะคุณสมบัติที่นับได้เท่านั้นที่จะถูกทำให้เป็นอนุกรม
// ไม่สามารถนับได้ คุณสมบัติจะถูกละเว้นโดยค่าเริ่มต้น // {"age":18} JSON.stringify( วัตถุสร้าง( โมฆะ, - ชื่อ: { ค่า: 'zcxiaobao', นับได้: false }, อายุ: { ค่า: 18, นับได้: จริง } - - );
ออบเจ็กต์ localStorage
ใช้เพื่อบันทึกข้อมูลของเว็บไซต์ทั้งหมดเป็นเวลานาน ข้อมูลที่บันทึกไว้ไม่มีเวลาหมดอายุจนกว่าจะถูกลบด้วยตนเอง โดยปกติแล้วเราจะเก็บมันไว้ในรูปแบบของวัตถุ
เพียงเรียกเมธอดอ็อบเจ็กต์ localStorage
const obj = { ชื่อ: 'zcxiaobao', อายุ: 18 - // เพียงแค่เรียก localStorage.setItem() localStorage.setItem('zc', obj); //ผลลัพธ์สุดท้ายที่ส่งคืนคือ [วัตถุวัตถุ] // จะเห็นได้ว่าการเรียก localStorage ล้มเหลว console.log(localStorage.getItem('zc'))
localStorage
ร่วมมือกับ JSON.stringify
เมธอด
localStorage.setItem('zc', JSON.stringify(obj)); //ผลลัพธ์สุดท้ายที่ส่งคืนคือ {ชื่อ: 'zcxiaobao' อายุ: 18} Console.log(JSON.parse(localStorage.getItem('zc')))
จะถือว่าสถานการณ์เช่นนี้ส่งคืนออบเจ็กต์แบบยาวที่มีแอตทริบิวต์จำนวนมาก และเราต้องการเพียงบางส่วนเท่านั้น และเราต้องจัดเก็บสิ่งเหล่านี้ คุณสมบัติใน localStorage
ตัวเลือกที่ 1: การกำหนดการทำลายโครงสร้าง + stringify
// เราต้องการเพียงแอตทริบิวต์ a, e, f const obj = { ก:1, b:2, ค:3, ง:4, อี:5, ฉ:6, ก:7 - // การกำหนดการทำลายโครงสร้าง const {a,e,f} = obj; // เก็บไปที่ localStorage localStorage.setItem('zc', JSON.stringify({a,e,f})) // {"ก":1,"อี":5,"ฉ":6} console.log (localstorage.getItem ('zc'))
ใช้พารามิเตอร์ replacer
ของ stringify
// ใช้ตัวแทนที่เป็นตัวกรองเป็นอาร์เรย์ localstorage.setItem ('zc', json.stringify (obj, ['a', 'e' , 'ฉ'])) // {"ก":1,"อี":5,"ฉ":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) => { ถ้า (typeof fn !== "ฟังก์ชั่น") { โยน TypeError ใหม่(`${fn} ไม่ใช่ฟังก์ชัน !`); - // ขั้นแรกให้เรียก JSON.stringify(obj, replacementr) เพื่อใช้ฟังก์ชัน map // จากนั้นเรียก JSON.parse เพื่อแปลงเป็น object return JSON.parse(JSON.stringify(obj, fn)); - // ตัวอย่างเช่น ค่าต่อไปนี้จะคูณค่าแอตทริบิวต์ของวัตถุ obj ด้วย 2 const obj = { ก: 1, ข: 2, ค: 3 - console.log (ObjectMap (obj, (คีย์, val) => { ถ้า (ประเภทของค่า === "หมายเลข") { ค่าส่งคืน * 2; - ค่าส่งคืน; }))
นักเรียนหลายคนอาจสงสัยว่าเหตุใดจึงต้องมีการตัดสินเพิ่มเติม เป็นไปไม่ได้ที่จะ return value * 2
ใช่ไหม
ดังที่ได้กล่าวไว้ข้างต้น ฟังก์ชัน replacer
แทนที่จะส่งผ่านในออบเจ็กต์ที่จะซีเรียลไลซ์ก่อน ออบเจ็กต์ * 2 => NaN => toJSON(NaN) => undefinit => จะถูกละเว้น และจะไม่มีการวิเคราะห์คู่คีย์-ค่าที่ตามมา
ด้วยฟังก์ชัน replacer
เรายังสามารถลบคุณลักษณะบางอย่างของวัตถุได้
const obj = { ชื่อ: 'zcxiaobao', อายุ: 18 - // {"อายุ":18} JSON.stringify(obj, (คีย์, วาล) => { // เมื่อไม่ได้กำหนดค่าที่ส่งคืน คุณสมบัตินี้จะถูกละเว้นหาก (key === 'name') { กลับไม่ได้กำหนด; - วาลส่งคืน; })
JSON.stringify
สามารถทำให้วัตถุเป็นอนุกรมเป็นสตริงได้ ดังนั้นเราจึงสามารถใช้วิธีสตริงเพื่อใช้การตัดสินความเท่าเทียมกันของวัตถุอย่างง่าย
//ตรวจสอบว่าอาร์เรย์มีวัตถุ const ชื่อ = [ {ชื่อ:'zcxiaobao'}, {ชื่อ:'txtx'}, {ชื่อ:'มี่'}, - const zcxiaobao = {ชื่อ:'zcxiaobao'}; // จริง JSON.stringify(ชื่อ).รวม(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}
แตกต่างกัน เราจึงจำเป็นต้องประมวลผลออบเจ็กต์ในอาร์เรย์ก่อนที่จะเริ่มต้น
วิธีที่ 1: จัดเรียงคีย์ของแต่ละออบเจ็กต์ในอาร์เรย์ตามลำดับพจนานุกรม
arr.forEach(item => { const รายการใหม่ = {}; Object.keys(item) // รับ object key value.sort() // Key value sorting.map(key => { // สร้างอ็อบเจ็กต์ใหม่ newItem[key] = item[key]; - // ใช้ newItem เพื่อดำเนินการขจัดความซ้ำซ้อน})
แต่วิธีที่หนึ่งยุ่งยากเล็กน้อย JSON.stringify
จัดเตรียมพารามิเตอร์รูปแบบอาร์เรย์ replacer
ซึ่งสามารถกรองอาร์เรย์ได้
วิธีที่ 2: ใช้ฟังก์ชันรูปแบบอาร์เรย์ replacer
เฉพาะ (arr) { const keySet = ชุดใหม่ (); const ที่ไม่ซ้ำกันObj = {} // แยกคีย์ทั้งหมด arr.forEach(item => { Object.keys(item).forEach(key => keySet.add(key)) - const แทนที่ = [...ชุดคีย์]; arr.forEach(รายการ => { // ออบเจ็กต์ทั้งหมดจะถูกกรองตามการแทนที่ค่าคีย์ที่ระบุ - กลับ Object.keys(unique).map(u => JSON.parse(u)) - //ทดสอบเฉพาะ([{}, {}, {เอ็กซ์:1}, {เอ็กซ์:1}, {a:1}, {x: 1, a: 1}, {เอ็กซ์:1,ก:1}, {x:1,a:1,b:1} - // ส่งคืนผลลัพธ์ [{ แก้ x":1 แก้ a":1 แก้ x":1,"a":1 แก้ x":1,"a":1 ,"ข":1}]