Câu 1Với const items = ["Bút", "Băng keo", "Giấy nhớ"];, length và index của phần tử cuối là bao nhiêu?
Mảng — Thêm, Xóa và Lấy Giá Trị
Học mảng: lưu nhiều giá trị trong một biến, lấy phần tử ra, thêm và xóa ở hai đầu, cắt và thay thế một phần.
Nếu bạn giữ từng món trong giỏ hàng ở một biến riêng — item1 / item2 / item3 — thì cứ thêm một sản phẩm là phải thêm một biến mới và sửa lại cả đoạn code đếm số món. Với một mảng (array) (cấu trúc dữ liệu lưu nhiều giá trị theo thứ tự trong một biến), bạn gom bao nhiêu món cũng được vào một biến duy nhất, còn việc thêm hay xóa thì để cho các phương thức có sẵn của mảng lo.
Lưu Giá Trị Trong Mảng — index và length
Bạn tạo một mảng bằng cách liệt kê các giá trị, ngăn cách bằng dấu phẩy, trong cặp [ và ]. Mỗi giá trị riêng lẻ gọi là một phần tử (element) (một giá trị nằm trong mảng). Chuỗi và số có thể để lẫn với nhau, nhưng trong thực tế người ta thường cho vào cùng một loại giá trị để phần xử lý về sau đồng nhất.
Muốn lấy một phần tử ra, bạn viết index của nó (vị trí của phần tử, đếm từ 0) trong []. Nếu viết cart[1] = "Băng keo" thì chỉ phần tử ở vị trí đó bị thay.
cart[0] là món thứ 1, cart[1] là món thứ 2. Vì món thứ 1 mang index 0 nên cách đánh số này lệch 1 so với cách người ta vẫn đếm.
length là số phần tử. Viết không có dấu ngoặc đơn, như cart.length. Vì index bắt đầu từ 0 nên index của phần tử cuối là length trừ đi 1.
at(-1) đếm ngược từ cuối, nhờ vậy bạn lấy được phần tử cuối mà không phải đi qua length. at(-2) là phần tử thứ 2 tính từ cuối.
Đọc một index ngoài phạm vi cũng không báo lỗi — bạn nhận về undefined, giá trị cho biết ở đó chưa có gì.
cart.length là 3 và index cuối là 2. Đếm từ cuối bằng at(-1) trả về cùng phần tử đó.const cart = ["Vở", "Bút", "Băng keo"];
console.log(cart[0]); // Vở
console.log(cart[2]); // Băng keo
console.log(cart.length); // 3
// Phần tử cuối nằm ở length trừ 1, hoặc dùng at(-1)
console.log(cart[cart.length - 1]); // Băng keo
console.log(cart.at(-1)); // Băng keo
// Đọc index ngoài phạm vi cũng không báo lỗi
console.log(cart[5]); // undefined
Thêm và Xóa Ở Cả Hai Đầu — push / pop / shift / unshift
Nội dung giỏ hàng thay đổi mỗi lần thêm hoặc bớt một món. Nếu lần nào bạn cũng viết lại mảng bằng tay thì phải tự quản lý số lượng và từng index. Mảng có sẵn các phương thức thêm và xóa phần tử ở cả hai đầu, nên việc tính toán vị trí có thể giao cho chúng.
push(value) thêm một phần tử vào cuối và trả về số phần tử sau khi thêm. pop() xóa phần tử cuối và trả về chính phần tử vừa bị xóa.
Muốn hiển thị tên món vừa bị xóa, bạn gán giá trị trả về của phương thức này vào một biến, như const removed = cart.pop();.
unshift(value) và shift() làm việc ở đầu mảng. unshift thêm vào đầu, còn shift xóa phần tử đầu. Khi bạn chèn một món vào đầu, index của mọi phần tử phía sau đều bị đẩy lùi 1 vị trí.
const cart = ["Bút"];
console.log(cart.push("Băng keo")); // 2 (số phần tử sau khi thêm)
console.log(cart.join(", ")); // Bút, Băng keo
console.log(cart.pop()); // Băng keo (phần tử vừa bị xóa)
console.log(cart.join(", ")); // Bút
cart.unshift("Giấy nhớ"); // thêm vào đầu
console.log(cart.join(", ")); // Giấy nhớ, Bút
console.log(cart.shift()); // Giấy nhớ (phần tử vừa bị xóa)
console.log(cart.join(", ")); // Bút
Có thể bạn thấy lạ khi cart khai báo bằng const mà nội dung vẫn thay đổi được. const chỉ cấm gán một giá trị khác cho chính tên biến đó, như cart = someOtherArray, chứ không ngăn bạn thay đổi nội dung bên trong mảng, như cart.push(value). Lý do có sự khác biệt này sẽ được giải thích ở bài sau.
Kiểm Tra Một Giá Trị Có Trong Mảng Không — includes và indexOf
Khi bạn thêm lại cùng một sản phẩm vào danh sách yêu thích mà chỉ gọi push, cùng một tên sẽ xuất hiện hai lần. Nếu kiểm tra xem giá trị đó đã có trong mảng chưa rồi mới thêm, bạn tránh được trùng lặp.
includes(value) trả về true nếu giá trị đó có trong mảng, false nếu không. Khi cần biết cả vị trí, bạn dùng indexOf(value) — nó trả về index của lần khớp đầu tiên. Không tìm thấy thì trả về -1.
Cách nghĩ giống hệt includes / indexOf của chuỗi, chỉ khác là ở đây bạn tìm một phần tử chứ không phải một ký tự.
Cả hai đều so sánh cả kiểu lẫn giá trị giống như ===, nên khác kiểu là coi như khác giá trị. Với một mảng toàn số, dù bạn tìm bằng chuỗi "2" thì cũng không có phần tử nào được coi là khớp.
["Giấy nhớ", "Bảng kẹp"]. Chỉ cần biết có hay không thì dùng includes, cần cả vị trí thì dùng indexOf. indexOf trả về -1 khi không tìm thấy.const favorites = ["Giấy nhớ", "Bảng kẹp"];
console.log(favorites.includes("Giấy nhớ")); // true
console.log(favorites.includes("Tẩy")); // false
console.log(favorites.indexOf("Bảng kẹp")); // 1
console.log(favorites.indexOf("Tẩy")); // -1
// Cách so sánh giống ===, khác kiểu là không khớp
const quantities = [1, 2, 3];
console.log(quantities.includes("2")); // false
Đừng Đưa Kết Quả indexOf Thẳng Vào Điều Kiện
Khi giá trị được tìm thấy ở vị trí đầu tiên, indexOf trả về 0. Vì 0 là falsy nên nếu viết if (favorites.indexOf(name)), đúng phần tử đứng đầu lại bị coi là "không có". Chỉ cần biết có hay không thì dùng includes; còn khi dùng indexOf, hãy so sánh kết quả một cách rõ ràng, như !== -1.
Lấy Ra và Thay Thế Một Phần Của Mảng — slice và splice
Có lúc bạn chỉ muốn hiện top 3 của bảng xếp hạng bán chạy, có lúc lại cần thay đúng một mục trong danh sách phương thức vận chuyển. Điểm khác nhau giữa hai việc này nằm ở chỗ bạn lấy ra mà vẫn giữ nguyên mảng gốc, hay thay đổi luôn chính mảng gốc.
slice(start, end) trả về một mảng mới gồm các phần tử từ index bắt đầu cho đến ngay trước index kết thúc. Mảng gốc không đổi.
Nếu bỏ qua tham số kết thúc, nó lấy đến hết mảng. Cách đếm giống hệt slice của chuỗi.
splice(start, deleteCount, itemsToAdd) thay đổi trực tiếp chính mảng gốc. Nó xóa deleteCount phần tử tính từ start, và nếu bạn viết thêm các giá trị cần chèn thì chúng được đưa vào đúng vị trí đó.
Giá trị trả về là một mảng chứa các phần tử đã bị xóa, nên khi chỉ xóa một phần tử, bạn đọc nội dung bằng [0].
| Cú pháp | Trả về | Mảng gốc |
|---|---|---|
| slice(start, end) | Mảng mới gồm các phần tử đã lấy ra | Không đổi |
| splice(start, deleteCount) | Mảng các phần tử đã bị xóa | Bị thay đổi |
| splice(start, deleteCount, itemsToAdd) | Mảng các phần tử đã bị xóa | Bị thay đổi |
const ranking = ["Bút", "Băng keo", "Giấy nhớ", "Bảng kẹp"];
// slice trả về mảng mới; ranking không đổi
const top2 = ranking.slice(0, 2);
console.log(top2.join(", ")); // Bút, Băng keo
console.log(ranking.length); // 4
// splice thay đổi trực tiếp ranking
const removed = ranking.splice(1, 2, "Tẩy");
console.log(removed.join(", ")); // Băng keo, Giấy nhớ
console.log(ranking.join(", ")); // Bút, Tẩy, Bảng kẹp
console.log(ranking.length); // 3
slice và splice Chỉ Khác 1 Chữ Nhưng Kết Quả Khác Nhau
Tên gọi na ná nhau, nhưng slice giữ nguyên mảng gốc và trả về một mảng mới, còn splice thì thay đổi luôn mảng gốc. Nếu chỉ để hiển thị một danh sách mà bạn lại gọi splice thì cứ mỗi lần hiển thị, dữ liệu gốc lại vơi đi một ít. Khi chỉ cần lấy ra, hãy chọn slice.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2cart.pop() trả về giá trị nào?
Câu 3Phương thức nào dưới đây không làm thay đổi mảng gốc khi được gọi?