[MẸO] Mảng Javascript: Mẹo và thủ thuật cần thiết
By hungpd, at: 12:01 Ngày 01 tháng 3 năm 2025
Thời gian đọc ước tính: __READING_TIME__ minutes
![[TIPS] Javascript Array: Essential Tips and Tricks](/media/filer_public_thumbnails/filer_public/4a/57/4a57ed90-3de4-4a17-a8d7-ca7670ffaead/javascript_array_-_tips_and_tricks.png__1500x900_crop_subsampling-2_upscale.png)
![[TIPS] Javascript Array: Essential Tips and Tricks](/media/filer_public_thumbnails/filer_public/4a/57/4a57ed90-3de4-4a17-a8d7-ca7670ffaead/javascript_array_-_tips_and_tricks.png__400x240_crop_subsampling-2_upscale.png)
[MẸO] Javascript Mảng: Mẹo và thủ thuật cần thiết
Mảng là một trong những cấu trúc dữ liệu được sử dụng phổ biến nhất trong JavaScript. Chúng cho phép chúng ta lưu trữ nhiều giá trị trong một biến duy nhất và đi kèm với các phương thức tích hợp mạnh mẽ giúp đơn giản hóa việc thao tác dữ liệu. Trong bài đăng trên blog này, chúng ta sẽ khám phá những mẹo cần thiết để làm việc với mảng hiệu quả, bao gồm các đoạn mã, giải thích và ưu điểm, nhược điểm của mỗi phương pháp.
1. Thêm và xóa phần tử
Push và Pop (Hành vi ngăn xếp)
-
push()
thêm một phần tử vào cuối mảng. -
pop()
xóa phần tử cuối cùng.
Ví dụ:
let arr = [1, 2, 3];
arr.push(4); // [1, 2, 3, 4]
arr.pop(); // [1, 2, 3]
Ưu điểm: Đơn giản và hiệu quả để sửa đổi cuối mảng.
Nhược điểm: Không hiệu quả đối với các mảng lớn nếu cần nhiều thao tác chèn/xóa.
Shift và Unshift (Hành vi hàng đợi)
-
unshift()
thêm một phần tử vào đầu mảng. -
shift()
xóa phần tử đầu tiên.
Ví dụ:
let arr = [1, 2, 3];
arr.unshift(0); // [0, 1, 2, 3]
arr.shift(); // [1, 2, 3]
Ưu điểm: Hữu ích để triển khai hàng đợi.
Nhược điểm: Chậm đối với các mảng lớn vì việc dịch chuyển các phần tử yêu cầu lập chỉ mục lại.
So sánh hiệu năng
// Ví dụ: So sánh hiệu năng của push/pop so với shift/unshift
let largeArray = Array.from({ length: 100000 }, (_, i) => i);
console.time("push/pop");
for (let i = 0; i < 1000; i++) {
largeArray.push(i);
largeArray.pop();
}
console.timeEnd("push/pop");
console.time("shift/unshift");
for (let i = 0; i < 1000; i++) {
largeArray.unshift(i);
largeArray.shift();
}
console.timeEnd("shift/unshift");
Giải thích: Như bạn có thể thấy từ các điểm chuẩn, các thao tác push
và pop
nhanh hơn đáng kể so với shift
và unshift
đối với các mảng lớn. Điều này là bởi vì shift
và unshift
yêu cầu lập chỉ mục lại tất cả các phần tử tiếp theo, đây là một thao tác tốn kém.
2. Sao chép mảng
Kỹ thuật Slice
Thay vì sửa đổi một mảng hiện có, bạn có thể muốn làm việc với một bản sao.
Ví dụ:
let arr = [1, 2, 3];
let copy1 = [...arr]; // Sử dụng toán tử spread
let copy2 = arr.slice(); // Sử dụng slice()
Ưu điểm: Cả hai phương pháp đều hiệu quả và ngăn chặn việc sửa đổi mảng gốc.
Nhược điểm: Không phù hợp để sao chép sâu (các mảng lồng nhau vẫn được tham chiếu).
Kỹ thuật sao chép sâu
// Ví dụ: Sao chép sâu sử dụng JSON.parse(JSON.stringify())
let nestedArray = [1, [2, 3], 4];
let deepCopy = JSON.parse(JSON.stringify(nestedArray));
deepCopy[1][0] = 99; // Sửa đổi bản sao
console.log(nestedArray); // Bản gốc không thay đổi
console.log(deepCopy); // Bản sao sâu phản ánh sự thay đổi
// Giải thích về những hạn chế:
// - Không hoạt động với hàm, undefined hoặc tham chiếu tuần hoàn.
// - Chậm hơn sao chép nông.
// Ví dụ sử dụng Lodash (nếu bạn muốn thêm):
// const _ = require('lodash');
// let deepCopyLodash = _.cloneDeep(nestedArray);
3. Các thao tác hàm trên mảng
Map, Filter và Reduce
-
map()
: Biến đổi mỗi phần tử. -
filter()
: Chọn các phần tử dựa trên một điều kiện. -
reduce()
: Tính toán một giá trị duy nhất dựa trên mảng.
Ví dụ:
let numbers = [1, 2, 3, 4];
let doubled = numbers.map(n => n * 2); // [2, 4, 6, 8]
let evens = numbers.filter(n => n % 2 === 0); // [2, 4]
let sum = numbers.reduce((acc, n) => acc + n, 0); // 10
Ưu điểm: Ngắn gọn, hiệu quả và thân thiện với lập trình hàm.
Nhược điểm: Tạo các mảng mới, có khả năng sử dụng nhiều bộ nhớ hơn.
Kỹ thuật nâng cao
// Ví dụ: Nhóm các đối tượng theo một thuộc tính
let people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 },
];
let groupedByAge = people.reduce((acc, person) => {
if (!acc[person.age]) {
acc[person.age] = [];
}
acc[person.age].push(person);
return acc;
}, {});
console.log(groupedByAge);
// Ví dụ: Tạo bản đồ tần số
let letters = ['a', 'b', 'a', 'c', 'b', 'b'];
let letterFrequency = letters.reduce((acc, letter) => {
acc[letter] = (acc[letter] || 0) + 1;
return acc;
}, {});
console.log(letterFrequency);
Giải thích: Phương thức reduce()
cực kỳ mạnh mẽ để biến đổi dữ liệu phức tạp, chẳng hạn như nhóm các đối tượng hoặc tạo bản đồ tần số.
4. Tìm kiếm phần tử trong mảng
Sử dụng find()
và findIndex()
-
find()
trả về phần tử đầu tiên khớp với điều kiện. -
findIndex()
trả về chỉ mục của phần tử khớp đầu tiên.
Ví dụ:
let users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
let user = users.find(u => u.id === 2); // { id: 2, name: 'Bob' }
let index = users.findIndex(u => u.id === 2); // 1
Ưu điểm: Dễ dàng tìm kiếm các đối tượng trong mảng.
Nhược điểm: Dừng lại ở lần khớp đầu tiên, vì vậy nó không lý tưởng cho nhiều kết quả.
5. Kiểm tra phần tử
Sử dụng includes()
Phương thức includes()
kiểm tra xem một mảng có chứa một giá trị cụ thể hay không.
Ví dụ:
let fruits = ['apple', 'banana', 'mango'];
console.log(fruits.includes('banana')); // true
Ưu điểm: Cú pháp đơn giản để kiểm tra sự tồn tại.
Nhược điểm: Chỉ hoạt động với các giá trị nguyên thủy (không phải đối tượng).
6. Xóa trùng lặp
Sử dụng Set
Một Set
tự động xóa các giá trị trùng lặp khỏi một mảng.
Ví dụ:
let numbers = [1, 2, 2, 3, 4, 4];
let unique = [...new Set(numbers)]; // [1, 2, 3, 4]
Ưu điểm: Đơn giản, nhanh và tích hợp sẵn.
Nhược điểm: Không hoạt động với các đối tượng (sử dụng map()
cho trường hợp đó).
7. Làm phẳng các mảng lồng nhau
Sử dụng flat()
Phương thức flat()
làm phẳng các mảng lồng nhau đến độ sâu được chỉ định.
Ví dụ:
let nested = [1, [2, 3], [4, [5, 6]]];
let flatArr = nested.flat(2); // [1, 2, 3, 4, 5, 6]
Ưu điểm: Giảm độ phức tạp khi xử lý các mảng lồng nhau sâu.
Nhược điểm: Chỉ hoạt động trên các trình duyệt hiện đại.
8. Sắp xếp mảng
Sử dụng sort()
(cẩn thận)
Theo mặc định, sort()
chuyển đổi các phần tử thành chuỗi trước khi sắp xếp, điều này có thể gây ra sự cố.
Ví dụ:
let numbers = [3, 1, 10, 5];
numbers.sort((a, b) => a - b); // [1, 3, 5, 10] (Tăng dần)
Ưu điểm: Linh hoạt để sắp xếp mảng theo bất kỳ thứ tự nào.
Nhược điểm: Nếu không có hàm so sánh, nó sẽ sắp xếp theo từ điển, điều này có thể không mong muốn.
Trường hợp biên cho sort()
// Ví dụ: trường hợp biên cho sort()
let mixedArray = [undefined, 1, 10, null, 5, 'a'];
mixedArray.sort((a,b) => {
if (a === undefined) return 1;
if (b === undefined) return -1;
if (a === null) return 1;
if (b === null) return -1;
if (typeof a === 'string') return 1;
if (typeof b === 'string') return -1;
return a - b;
});
console.log(mixedArray);
9. Chuyển đổi chuỗi thành mảng
Sử dụng split()
Một trường hợp sử dụng phổ biến là chia một chuỗi thành một mảng.
Ví dụ:
let str = "hello world";
let words = str.split(" "); // ["hello", "world"]
Ưu điểm: Hữu ích để phân tích và thao tác văn bản.
Nhược điểm: Yêu cầu một dấu phân cách và không xử lý tốt nhiều khoảng trắng.
Suy nghĩ cuối cùng
JavaScript mảng rất mạnh mẽ và linh hoạt. Bằng cách nắm vững những mẹo này, bạn có thể viết mã sạch hơn và hiệu quả hơn.