angular - โคลนอาเรย์ใน Javascript/Typescript
ฉันมีอาเรย์ของวัตถุสองตัว:
genericItems: Item[] = [];
backupData: Item[] = [];
ฉันกำลังเติมข้อมูลในตาราง HTML ของฉันด้วยข้อมูล genericItems ตารางนี้สามารถแก้ไขได้ มีปุ่มรีเซ็ตเพื่อเลิกทำการเปลี่ยนแปลงทั้งหมดที่ทำด้วย backUpData อาร์เรย์นี้ถูกเติมโดยบริการ:
getGenericItems(selected: Item) {
this.itemService.getGenericItems(selected).subscribe(
result => {
this.genericItems = result;
});
this.backupData = this.genericItems.slice();
}
ความคิดของฉันคือ การเปลี่ยนแปลงของผู้ใช้จะสะท้อนในอาร์เรย์แรก และอาร์เรย์ที่สองสามารถใช้เป็นสำรองสำหรับการรีเซ็ต ปัญหาที่ฉันพบที่นี่คือเมื่อผู้ใช้แก้ไขตาราง (genericItems[]) อาร์เรย์ที่สอง backupData ก็ถูกแก้ไขด้วยเช่นกัน
นี่เกิดขึ้นได้อย่างไรและจะป้องกันได้อย่างไร?
คำตอบ #1
โคลนวัตถุ:
const myClonedObject = Object.assign({}, myObject); โคลนอาเรย์:
- ตัวเลือก 1 ถ้าคุณมีอาร์เรย์ของชนิดข้อมูลพื้นฐาน:
const myClonedArray = Object.assign([], myArray);- ตัวเลือก 2 - ถ้าคุณมีอาร์เรย์ของวัตถุ:
const myArray= [{ a: 'a', b: 'b' }, { a: 'c', b: 'd' }];
const myClonedArray = [];
myArray.forEach(val => myClonedArray.push(Object.assign({}, val)));
คำตอบ #2
การโคลนอาร์เรย์และออบเจ็กต์ใน JavaScript มีไวยากรณ์ที่แตกต่างกัน เร็วหรือช้าทุกคนก็จะเรียนรู้ความแตกต่างในวิธีที่ยากและลงเอยที่นี่
ใน Typescript และ ES6 คุณสามารถใช้ตัวดำเนินการแพร่กระจายสำหรับอาร์เรย์และอ็อบเจ็กต์:
const myClonedArray = [...myArray]; // This is ok for [1,2,'test','bla']
// But wont work for [{a:1}, {b:2}].
// A bug will occur when you
// modify the clone and you expect the
// original not to be modified.
// The solution is to do a deep copy
// when you are cloning an array of objects.
ในการทำสำเนาลึกของวัตถุ คุณต้องใช้ไลบรารีภายนอก:
import {cloneDeep} from 'lodash';
const myClonedArray = cloneDeep(myArray); // This works for [{a:1}, {b:2}]
โอเปอเรเตอร์การกระจายทำงานกับอ็อบเจ็กต์ได้เช่นกัน แต่จะทำการคัดลอกแบบตื้นเท่านั้น (ชั้นแรกของลูก)
const myShallowClonedObject = {...myObject}; // Will do a shallow copy
// and cause you an un expected bug.
ในการทำสำเนาลึกของวัตถุ คุณต้องใช้ไลบรารีภายนอก:
import {cloneDeep} from 'lodash';
const deeplyClonedObject = cloneDeep(myObject); // This works for [{a:{b:2}}]
คำตอบ #3
การใช้ map หรือวิธีการคล้ายกันอื่น ๆ ไม่ช่วยในการคัดลอกแบบลึกของอาร์เรย์ของออบเจ็กต์ วิธีที่ง่ายกว่าที่จะทำสิ่งนี้โดยไม่ต้องเพิ่มไลบรารีใหม่คือการใช้ JSON.stringify แล้วตามด้วย JSON.parse
ในกรณีของคุณ สิ่งนี้ควรใช้ได้ :
this.backupData = JSON.parse(JSON.stringify(genericItems));
- เมื่อใช้เวอร์ชันล่าสุดของ JS/TS การติดตั้งไลบรารีขนาดใหญ่เช่น lodash เพียงเพื่อฟังก์ชันหนึ่งหรือสองฟังก์ชันถือเป็นการตัดสินใจที่ไม่ดี คุณจะทำให้ประสิทธิภาพของแอปลดลง และในระยะยาวคุณจะต้องดูแลการอัปเกรดไลบรารี! ตรวจสอบได้ที่ https://bundlephobia.com/
สำหรับอ็อบเจ็กต์ขนาดเล็ก lodash cloneDeep อาจจะเร็วกว่า แต่สำหรับอ็อบเจ็กต์ที่ใหญ่ขึ้น/ซับซ้อนขึ้น json clone จะเร็วกว่ามาก ดังนั้นในกรณีเหล่านี้คุณไม่ควรลังเลที่จะใช้มัน ตรวจสอบได้ที่ https://www.measurethat.net/Benchmarks/Show/6039/0/lodash-clonedeep-vs-json-clone-larger-object และสำหรับข้อมูลเพิ่มเติม https://v8.dev/blog/cost-of-javascript-2019#json
ความไม่สะดวกคือวัตถุต้นทางของคุณต้องสามารถแปลงเป็น JSON ได้
คำตอบ #4
ลองใช้โค้ดต่อไปนี้:
this.cloneArray= [...this.OriginalArray]
