Trường hợp mảng gồm các phần tử có kiểu dữ liệu primitive
javascript có 7 kiểu primitive là string, number, boolean, undefined, null, symbol, bigint.
const input = [3, 5, 7, 9];
1. Dùng toán tử spread
const outputA1 = [...input];
2. Dùng slice không đối số
const outputA2 = input.slice();
Đây là 2 cách tốt nhất, không những tối ưu về mặt hiệu năng mà cú pháp lại ngắn.
3. Một số cách khác
const outputA3 = [].concat(input);
const outputA4 = Array.from(input);
const outputA5 = input.map(x => x);
const outputA6 = input.filter(() => true);
4. Sử dụng thư viện lodash
const outputA7 = _.clone(input);
5. Không nên dùng JSON.parse
&JSON.stringify
để copy mảng chỉ chứa phần tử primitive
const outputA8 = JSON.parse(JSON.stringify(input));
Nó vẫn chạy và trả về kết quả như mong muốn nhưng không cần phải "dùng dao mổ trâu giết gà". Cách này nên sử dụng để copy object hoặc mảng object.
Nhưng đời không như lý thuyết, thực tế rất ít trường hợp thao tác với mảng chỉ toàn kiểu dữ liệu primitive mà chỉ toàn thao tác với mảng object.
Trường hợp mảng gồm các phần tử là object
const input2 = [{ name: "tony", age: 18 }, { name: "lisa", age: 19 }]
Đối với mảng chứa các phần tử là object nếu sử dụng những cách bên trên thì mảng mới được tạo ra chỉ chứa địa chỉ ô nhớ của các phần tử trước đó. Những phần tử là object ban đầu (lưu trong heap) vẫn chỉ có 1, chỉ là địa chỉ ô nhớ của nó được copy mà thôi, trường hợp này gọi là shallow copy, dịch ra tiếng Việt là copy một phần, copy không hoàn toàn,... Đây là kiểu copy không không an toàn vì nó vẫn còn "lưu luyến" với object cũ, thay đổi thuộc tính của phần tử trong mảng mới lại khiến mảng cũ bị thay đổi theo vì cùng trỏ tới 1 object trong heap.
Để xử lý trường hợp này ta cần phải tìm giải pháp deep copy được hiểu như cách mà bạn đang kỳ vọng, copy theo đúng nghĩa đen, copy hoàn toàn một object, không liên quan gì đến mảng cũ cả.
1. Sử dung hàm structuredClone
, đây là nativeAPI phạm vi global có sẵn trong javascript hỗ trợ hầu hết mọi trình duyệt ngoài trừ Internet Explorer & Samsung Internet.
const outputB1 = structuredClone(input2);
2. Đây mới là lúc sử dụng JSON.parse & JSON.stringify
const outputB2 = JSON.parse(JSON.stringify(input2));
3. Sử dụng lodash
const outputB3 = _cloneDeep(input2);
Trường hợp copy object có thuộc tính là các object lồng nhau nhiều cấp
const input3 = {
name: "lisa",
location: {
province: {
id: "HCM",
name: "HCM City",
zipCode: 700000
}
}
}
Đây là lúc thích hợp sử dụng JSON.parse
/JSON.stringify
như ưu tiên sử dụng structuredClone
để giải quyết trường hợp này.
const outputC1 = structuredClone(input3);
const outputC2 = JSON.parse(JSON.stringify(input3));
Tóm lại deep clone object bằng structuredClone
là cách chính thống nhất. JSON.parse
& JSON.stringify
trông có vẻ như một trick, mẹo vặt.
JSON.parse
/JSON.stringify
có nhược điểm là không thể copy object trong trường hợp thuộc tính của object đó là functions, undefined , Infinity , NaN, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Array, ...
Nếu bạn hiểu rõ cơ chế lưu data vào bộ nhớ stack, heap, nguyên lý phép gán, truyền tham chiếu, truyền tham trị, ... thì sẽ dễ dàng phân biệt được shallow copy và deep copy. Mình sẽ giải thích kỹ phần này ở post tiếp theo. Cái mà bạn nên quan tâm là phương pháp copy nào mang lại hiệu năng cao nhất, nhanh nhất khi xử lý một mảng khổng lồ mà API trả về, ví dụ như API này: https://finfo-api.vndirect.com.vn/v4/stock_prices?sort=date%3Adesc&q=floor%3AHOSE%2CHNX~type%3ASTOCK&fields=code%2Cdate%2Copen%2Chigh%2Clow%2Cclose%2CnmVolume%2Cchange%2CpctChange&size=100000&page=1