javascript - Sao chép sâu trong ES6 sử dụng cú pháp spread
Tôi đang cố gắng tạo một phương thức bản đồ sao chép sâu cho dự án Redux của mình mà sẽ hoạt động với các đối tượng thay vì mảng. Tôi đọc rằng trong Redux, mỗi trạng thái không nên thay đổi bất cứ thứ gì trong các trạng thái trước đó.
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, {...object[key]});
return output;
},
{});
}
Nó hoạt động:
return mapCopy(state, e => {
if (e.id === action.id) {
e.title = 'new item';
}
return e;
})
Tuy nhiên nó không sao chép sâu các mục bên trong nên tôi cần điều chỉnh nó để:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
let newObject = {...object[key]};
newObject.style = {...newObject.style};
newObject.data = {...newObject.data};
output[key] = callback.call(this, newObject);
return output;
}, {});
}
Điều này kém tinh tế hơn vì nó đòi hỏi phải biết các đối tượng nào được truyền. Có cách nào trong ES6 để sử dụng cú pháp spread để sao chép sâu một đối tượng không?
Câu trả lời #1
Sử dụng JSON để sao chép sâu
var newObject = JSON.parse(JSON.stringify(oldObject))
var oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));
newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);Câu trả lời #2
Không có chức năng như vậy được tích hợp sẵn trong ES6. Tôi nghĩ bạn có một vài lựa chọn tùy thuộc vào việc bạn muốn làm gì.
Nếu bạn thực sự muốn sao chép sâu:
- Sử dụng một thư viện. Ví dụ, lodash có phương thức
cloneDeep. - Thực hiện chức năng nhân bản của riêng bạn.
Giải pháp thay thế cho vấn đề cụ thể của bạn (Không sao chép sâu)
Tuy nhiên, tôi nghĩ rằng, nếu bạn sẵn sàng thay đổi một vài thứ, bạn có thể tiết kiệm cho mình một số công việc. Tôi đang giả sử rằng bạn kiểm soát tất cả các điểm gọi tới hàm của bạn.
Chỉ rõ rằng tất cả các callback được truyền vào
mapCopyphải trả về các đối tượng mới thay vì thay đổi đối tượng hiện có. Ví dụ:mapCopy(state, e => { if (e.id === action.id) { return Object.assign({}, e, { title: 'new item' }); } else { return e; } });Điều này sử dụng
Object.assignđể tạo một đối tượng mới, đặt các thuộc tính củaelên đối tượng mới đó, sau đó đặt một tiêu đề mới trên đối tượng mới này. Điều này có nghĩa là bạn không bao giờ thay đổi các đối tượng hiện có và chỉ tạo các đối tượng mới khi cần thiết.mapCopygiờ có thể thực sự đơn giản:export const mapCopy = (object, callback) => { return Object.keys(object).reduce(function (output, key) { output[key] = callback.call(this, object[key]); return output; }, {}); }
Về cơ bản, mapCopy đang tin tưởng các caller của nó thực hiện đúng việc. Đây là lý do tại sao tôi nói điều này giả định bạn kiểm soát tất cả các điểm gọi.
Câu trả lời #3
Từ MDN
Lưu ý: Cú pháp spread thực sự chỉ sao chép sâu một cấp khi sao chép một mảng. Do đó, nó có thể không phù hợp để sao chép các mảng đa chiều như ví dụ sau đây cho thấy (nó cũng giống với Object.assign() và cú pháp spread).
Cá nhân, tôi gợi ý sử dụng hàm cloneDeep của Lodash để sao chép đối tượng/mảng nhiều cấp.
Dưới đây là một ví dụ hoạt động:
const arr1 = [{ 'a': 1 }];
const arr2 = [...arr1];
const arr3 = _.clone(arr1);
const arr4 = arr1.slice();
const arr5 = _.cloneDeep(arr1);
const arr6 = [...{...arr1}]; // a bit ugly syntax but it is working!
// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false
// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>Câu trả lời #4
Tôi thường sử dụng cái này:
function deepCopy(obj) {
if(typeof obj !== 'object' || obj === null) {
return obj;
}
if(obj instanceof Date) {
return new Date(obj.getTime());
}
if(obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepCopy(item);
return arr;
}, []);
}
if(obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepCopy(obj[key]);
return newObj;
}, {})
}
}
Câu trả lời số 5
Bạn có thể sử dụng structuredClone() như sau:
const myOriginal = {
title: "Full Stack JavaScript Developer",
info: {
firstname: "Javascript",
surname: " Addicted",
age: 34
}
};
const myDeepCopy = structuredClone(myOriginal);
structuredClone()
Bạn có thể sử dụng structuredClone() là một hàm tích hợp sẵn để sao chép sâu. Sao chép có cấu trúc giải quyết nhiều (mặc dù không phải tất cả) những hạn chế của kỹ thuật JSON.stringify(). Sao chép có cấu trúc có thể xử lý các cấu trúc dữ liệu tuần hoàn, hỗ trợ nhiều kiểu dữ liệu tích hợp, và nói chung mạnh mẽ hơn và thường nhanh hơn.
Tuy nhiên, nó vẫn có một số hạn chế có thể làm bạn bất ngờ:
1-Mẫu thử : Nếu bạn sử dụng structuredClone() với một thể hiện lớp, bạn sẽ nhận được một đối tượng thuần túy làm giá trị trả về, bởi vì nhân bản có cấu trúc sẽ loại bỏ chuỗi nguyên mẫu của đối tượng.
2-Chức năng: Nếu đối tượng của bạn chứa các chức năng, chúng sẽ bị loại bỏ một cách lặng lẽ.
3- Không thể nhân bản: Một số giá trị không thể nhân bản theo cấu trúc, đáng chú ý nhất là Error và các nút DOM. Điều này sẽ khiến structuredClone() ném lỗi.
const myDeepCopy = structuredClone(myOriginal);
JSON.stringify
Nếu bạn chỉ đơn giản muốn sao chép sâu đối tượng sang một đối tượng khác, tất cả những gì bạn cần làm là JSON.stringify đối tượng và sau đó phân tích cú pháp nó bằng JSON.parse. Điều này về cơ bản sẽ thực hiện sao chép sâu của đối tượng.
let user1 = {
name: 'Javascript Addicted',
age: 34,
university: {
name: 'Shiraz Bahonar University'
}
};
let user2 = JSON.parse(JSON.stringify(user1));
user2.name = 'Andy Madadian';
user2.university.name = 'Kerman Bahonar University'
console.log(user2);
// { name: 'Andy Madadian', age: 33, university: { name: 'Kerman Bahonar University' } }
console.log(user1);
// { name: 'Javascript Addicted', age: 33, university: { name: 'Shiraz Bahonar University' } }
Toán tử spread/Object.assign()
Một cách để tạo bản sao nông trong JavaScript bằng cách sử dụng toán tử spread của đối tượng ... hoặc Object.assign() như sau:
const myShallowCopySpread = {...myOriginal};
const myShallowCopyObjectAssign=Object.assign({},obj)
Hiệu suất
Khi nói đến hiệu suất, người sáng tạo Surma đã chỉ ra rằng JSON.Parse() có thể nhanh hơn một chút đối với các đối tượng nhỏ. Nhưng khi bạn có một đối tượng lớn, đối tượng phức tạp, structuredClone() bắt đầu trở nên nhanh hơn đáng kể.
Hỗ trợ trình duyệt khá tuyệt vời và thậm chí còn được Node.js hỗ trợ.
