ตอนนี้แพลตฟอร์มจะจัดส่งผลิตภัณฑ์ด้วย StructuredClone() ซึ่งเป็นฟังก์ชันในตัวสำหรับการทำสำเนาแบบเจาะลึก
เป็นเวลานานที่สุด คุณต้องใช้วิธีแก้ปัญหาชั่วคราวและไลบรารีเพื่อสร้างสำเนาเชิงลึกของค่า JavaScript ตอนนี้แพลตฟอร์มจะจัดส่งมาพร้อม structuredClone()
ซึ่งเป็นฟังก์ชันในตัวสำหรับการทำสำเนาแบบเจาะลึก
สำเนาตื้น
การคัดลอกค่าใน JavaScript จะเป็นแบบตื้นเกือบทุกครั้ง ซึ่งต่างจากการคัดลอกระดับลึก ซึ่งหมายความว่าการเปลี่ยนแปลงค่าที่ฝังลึกไว้จะปรากฏในสำเนาเช่นเดียวกับต้นฉบับ
วิธีหนึ่งในการสร้างสำเนาแบบสั้นใน JavaScript โดยใช้โอเปอเรเตอร์การกระจายออบเจ็กต์ ...
ได้แก่
const myOriginal = {
someProp: "with a string value",
anotherProp: {
withAnotherProp: 1,
andAnotherProp: true
}
};
const myShallowCopy = {...myOriginal};
การเพิ่มหรือเปลี่ยนคุณสมบัติโดยตรงในข้อความขนาดเล็กจะมีผลกับสำเนาเท่านั้น แต่จะไม่มีผลกับต้นฉบับ
myShallowCopy.aNewProp = "a new value";
console.log(myOriginal.aNewProp)
// ^ logs `undefined`
อย่างไรก็ตาม การเพิ่มหรือเปลี่ยนแปลงพร็อพเพอร์ตี้ที่ฝังอยู่ลึกจะส่งผลต่อทั้งสำเนาและข้อมูลต้นฉบับ ดังนี้
myShallowCopy.anotherProp.aNewProp = "a new value";
console.log(myOriginal.anotherProp.aNewProp)
// ^ logs `a new value`
นิพจน์ {...myOriginal}
จะทำซ้ำในคุณสมบัติ (แจกแจงได้) ของ myOriginal
โดยใช้โอเปอเรเตอร์การแพร่กระจาย พร็อพเพอร์ตี้นี้จะใช้ชื่อและค่าของพร็อพเพอร์ตี้ และกำหนดให้กับออบเจ็กต์เปล่าที่สร้างขึ้นใหม่ทีละรายการ ดังนั้น วัตถุที่ได้จะมีรูปร่างเหมือนกัน แต่มีสำเนาของรายการคุณสมบัติและค่าเป็นของตัวเอง ระบบจะคัดลอกค่าดังกล่าวด้วย แต่ค่า JavaScript ดังกล่าวจะได้รับการจัดการต่างจากค่าที่ไม่ใช่ค่าพื้นฐาน วิธีอ้างอิง MDN
ใน JavaScript ค่าพื้นฐาน (ค่าพื้นฐาน, ประเภทข้อมูลเบื้องต้น) คือข้อมูลที่ไม่ใช่ออบเจ็กต์และไม่มีเมธอด ประเภทข้อมูลพื้นฐานมีอยู่ 7 ประเภท ได้แก่ สตริง ตัวเลข บิ้นต์ บูลีน ไม่ระบุ สัญลักษณ์ และ Null
MDN - พื้นฐาน
ค่าที่ไม่ใช่ค่าพื้นฐานจะได้รับการจัดการเป็นreferences ซึ่งหมายความว่าการคัดลอกค่าเป็นเพียงการคัดลอกการอ้างอิงไปยังออบเจ็กต์พื้นฐานเดียวกันเท่านั้น ซึ่งส่งผลให้การคัดลอกเป็นแบบตื้น
สำเนาแบบ Deep
ส่วนตรงข้ามกับข้อความตื้น ๆ คือข้อความที่มีความลึกซึ้ง อัลกอริทึมการคัดลอกเชิงลึกยังคัดลอกคุณสมบัติของวัตถุทีละรายการด้วย แต่จะเรียกตัวเองซ้ำๆ เมื่อพบการอ้างอิงไปยังวัตถุอื่น ซึ่งจะสร้างสำเนาของวัตถุนั้นด้วยเช่นกัน ซึ่งเป็นเรื่องสำคัญมากที่จะตรวจสอบว่าโค้ด 2 ชุดไม่ได้แชร์วัตถุกันโดยไม่ได้ตั้งใจและปรับเปลี่ยนสถานะของกันและกันโดยไม่รู้ตัว
ก่อนหน้านี้ ยังไม่มีวิธีที่ง่ายหรือดีในการสร้างสำเนาค่าใน JavaScript ผู้ใช้จำนวนมากใช้ไลบรารีของบุคคลที่สาม เช่น ฟังก์ชัน cloneDeep()
ของ Lodash วิธีแก้ปัญหาที่ใช้กันมากที่สุดสำหรับปัญหานี้คือการแฮ็กที่ใช้ JSON ดังนี้
const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));
อันที่จริง วิธีนี้เป็นวิธีแก้ปัญหาชั่วคราวที่นิยมใช้กัน กล่าวคือ V8 เพิ่มประสิทธิภาพในเชิงรุก JSON.parse()
และโดยเฉพาะรูปแบบข้างต้นเพื่อทำให้รวดเร็วที่สุด และแม้ว่าจะรวดเร็ว แต่ก็มาพร้อมจุดบกพร่องและ 3 แบบ ได้แก่
- โครงสร้างข้อมูลที่เกิดซ้ำ:
JSON.stringify()
จะแสดงเมื่อคุณกำหนดโครงสร้างข้อมูลที่เกิดซ้ำ กรณีนี้อาจเกิดขึ้นค่อนข้างง่ายเมื่อใช้รายการหรือโครงสร้างที่เชื่อมโยง - ประเภทในตัว:
JSON.stringify()
จะโยนหากค่ามี JS ในตัวอื่นๆ เช่นMap
,Set
,Date
,RegExp
หรือArrayBuffer
- ฟังก์ชัน:
JSON.stringify()
จะทิ้งฟังก์ชันอย่างเงียบๆ
การโคลนที่มีโครงสร้าง
แพลตฟอร์มนี้ต้องการความสามารถในการสร้างสำเนาแบบลึกของค่า JavaScript ใน 2 ตำแหน่งอยู่แล้ว การจัดเก็บค่า JS ใน IndexedDB ต้องใช้รูปแบบการเรียงอันดับบางอย่างเพื่อให้จัดเก็บไว้ในดิสก์ได้และทำการดีซีเรียลไลซ์เพื่อกู้คืนค่า JS ในภายหลัง ในทำนองเดียวกัน การส่งข้อความไปยัง WebWorker ผ่าน postMessage()
จะต้องมีการโอนค่า JS จากขอบเขต JS หนึ่งไปยังขอบเขตอื่น อัลกอริทึมที่ใช้สำหรับขั้นตอนนี้เรียกว่า "การโคลนที่มีโครงสร้าง" และก่อนหน้านี้นักพัฒนาแอปดังกล่าวยังเข้าถึงได้ยากเมื่อเร็วๆ นี้
แต่ตอนนี้เปลี่ยนไปแล้ว มีการแก้ไขข้อกำหนดของ HTML เพื่อแสดงฟังก์ชัน structuredClone()
ซึ่งเรียกใช้อัลกอริทึมดังกล่าวเพื่อให้นักพัฒนาซอฟต์แวร์สามารถสร้างสำเนาค่า JavaScript แบบเจาะลึกได้อย่างง่ายดาย
const myDeepCopy = structuredClone(myOriginal);
เท่านี้ก็เรียบร้อย ทั้งหมดนี้คือ API หากต้องการเจาะลึกลงไปในรายละเอียด โปรดอ่านบทความ MDN
ฟีเจอร์และข้อจำกัด
การโคลนที่มีโครงสร้างจะช่วยแก้ปัญหาจุดบกพร่องของเทคนิค JSON.stringify()
จำนวนมาก (แต่ไม่ใช่ทั้งหมด) การโคลนที่มีโครงสร้างสามารถจัดการโครงสร้างข้อมูลแบบวนซ้ำ รองรับข้อมูลในตัวหลายประเภท และโดยทั่วไปมีประสิทธิภาพมากกว่าและมักจะทำงานได้เร็วกว่า
แต่ก็มีข้อจำกัดบางประการที่ทำให้คุณเข้าใจผิด เช่น
- ต้นแบบ: หากใช้
structuredClone()
กับอินสแตนซ์คลาส คุณจะได้รับออบเจ็กต์แบบธรรมดาเป็นค่าที่แสดงผล เนื่องจากการโคลนที่มีโครงสร้างจะยกเลิกห่วงโซ่ต้นแบบของออบเจ็กต์ - ฟังก์ชัน: หากออบเจ็กต์มีฟังก์ชัน
structuredClone()
จะส่งข้อยกเว้นDataCloneError
- โคลนไม่ได้: ค่าบางค่าไม่สามารถโคลนที่มีโครงสร้างได้ โดยเฉพาะ
Error
และโหนด DOM เพราะจะทำให้โยนstructuredClone()
หากข้อจำกัดข้อใดข้อหนึ่งเหล่านี้เป็นตัวทำลายดีลสำหรับกรณีการใช้งานของคุณ ไลบรารีอย่าง Lodash จะยังคงมีการใช้งานที่กำหนดเองของอัลกอริทึมการโคลนในรายละเอียดอื่นๆ ซึ่งอาจหรือไม่เหมาะสมกับกรณีการใช้งานของคุณ
การแสดง
แม้จะยังไม่ได้ทำการเปรียบเทียบเกณฑ์เปรียบเทียบย่อยใหม่ แต่ฉันทำการเปรียบเทียบในช่วงต้นปี 2018 ก่อนที่ structuredClone()
จะแสดง สมัยนั้น JSON.parse()
เป็นตัวเลือกที่เร็วที่สุดสำหรับวัตถุขนาดเล็กมาก เราคาดว่าจะยังคงเหมือนเดิม เทคนิคที่อาศัยการโคลนที่มีโครงสร้างคือ (สำคัญมาก) สำหรับวัตถุขนาดใหญ่ เนื่องจาก structuredClone()
ใหม่ไม่มีค่าใช้จ่ายในการละเมิด API อื่นๆ และมีความปลอดภัยมากกว่า JSON.parse()
เราจึงขอแนะนำให้คุณกำหนดเป็นค่าเริ่มต้นสำหรับการสร้างสำเนาแบบเจาะลึก
บทสรุป
หากคุณจำเป็นต้องสร้างสำเนาค่าใน JS แบบเจาะลึก อาจเป็นเพราะคุณใช้โครงสร้างข้อมูลที่เปลี่ยนแปลงไม่ได้ หรือต้องการตรวจสอบว่าฟังก์ชันสามารถจัดการกับออบเจ็กต์ได้โดยไม่ส่งผลกระทบต่อต้นฉบับ คุณไม่จำเป็นต้องเข้าถึงวิธีแก้ปัญหาเฉพาะหน้าหรือไลบรารีอีกต่อไป ตอนนี้ระบบนิเวศ JS มี structuredClone()
แล้ว ฮุซซาห์