Đối tượng cho phép bạn lưu trữ các bộ sưu tập giá trị có từ khóa(thuộc tính).

Nhưng khá thường xuyên chúng ta thấy rằng chúng ta cần một bộ sưu tập theo thứ tự , trong đó chúng ta có phần tử thứ 1, thứ 2, thứ 3, v.v. Ví dụ: chúng ta cần điều đó để lưu trữ một danh sách một cái gì đó: người dùng, hàng hóa, các yếu tố HTML, v.v.

Không thuận tiện khi sử dụng một đối tượng ở đây, vì nó không cung cấp các phương thức để quản lý thứ tự các phần tử. Chúng ta không thể chèn một thuộc tính mới giữa các ứng dụng hiện có.

Tồn tại một cấu trúc dữ liệu đặc biệt có tên Array, để lưu trữ các bộ sưu tập theo thứ tự.

Khai báo

Có hai cú pháp để tạo một mảng trống:

let arr = new Array();
let arr = [];

Hầu như tất cả thời gian, cú pháp thứ hai được sử dụng. Chúng ta có thể cung cấp các yếu tố ban đầu trong ngoặc:

let fruits = ["Apple", "Orange", "Plum"];

Các phần tử mảng được đánh số, bắt đầu bằng không.

Chúng ta có thể lấy một phần tử bằng số của nó trong ngoặc vuông:

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits[0] ); // Apple
alert( fruits[1] ); // Orange
alert( fruits[2] ); // Plum

Chúng ta có thể thay thế một yếu tố:




fruits[2] = 'Pear'; // now ["Apple", "Orange", "Pear"]

Khác hoặc thêm một cái mới vào mảng:

fruits[3] = 'Lemon'; // now ["Apple", "Orange", "Pear", "Lemon"]

Tổng số các phần tử trong mảng là length:

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits.length ); // 3

Chúng ta cũng có thể sử dụng alertđể hiển thị toàn bộ mảng.

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits ); // Apple,Orange,Plum

Một mảng có thể lưu trữ các phần tử của bất kỳ kiểu.

Ví dụ:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

// mix of values
let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];

// get the object at index 1 and then show its name
alert( arr[1].name ); // John

// get the function at index 3 and run it
arr[3](); // hello

Dấu phẩy

Một mảng, giống như một đối tượng, có thể kết thúc bằng dấu phẩy:

let fruits = [
  "Apple",
  "Orange",
  "Plum",
];

Kiểu dấu phẩy trong khởi tạo trên làm cho việc chèn / xóa các mục dễ dàng hơn vì tất cả các dòng đều giống nhau.

Phương thức pop/push, shift/unshift

Một hàng đợi là một trong những ứng dụng phổ biến nhất của một mảng. Trong khoa học máy tính, điều này có nghĩa là một tập hợp các yếu tố hỗ trợ hai thao tác:

  • push nối một phần tử vào cuối
  • shift lấy một phần tử từ đầu, tiến lên hàng đợi, để phần tử thứ 2 trở thành phần tử thứ nhất.

Mảng hỗ trợ cả hai hoạt động.




Trong thực tế chúng ta cần nó rất thường xuyên. Ví dụ, một hàng các tin nhắn cần được hiển thị trên màn hình.

Có một trường hợp sử dụng khác cho mảng – cấu trúc dữ liệu có tên stack.

Nó hỗ trợ hai hoạt động:

  • push thêm một yếu tố vào cuối
  • pop mất một yếu tố từ cuối.

Vì vậy, các yếu tố mới được thêm vào hoặc lấy luôn từ phần cuối của stack.

Một ngăn xếp(stack) thường được minh họa dưới dạng một gói thẻ: thẻ mới được thêm vào đầu hoặc được lấy từ đầu:

Đối với ngăn xếp, vật phẩm đẩy mới nhất được nhận trước tiên, đó cũng được gọi là nguyên tắc LIFO (Last-In-First-Out). Đối với hàng đợi, chúng ta có FIFO (First-In-First-Out).

Mảng trong JavaScript có thể hoạt động như một hàng đợi và như một ngăn xếp. Chúng cho phép bạn thêm / xóa các phần tử cả đến / từ đầu hoặc cuối.

Trong khoa học máy tính, cấu trúc dữ liệu cho phép điều này, được gọi là deque .

Các phương thức hoạt động với phần cuối của mảng:pop

Trích xuất phần tử cuối cùng của mảng và trả về nó:




let fruits = ["Apple", "Orange", "Pear"];

alert( fruits.pop() ); // remove "Pear" and alert it

alert( fruits ); // Apple, Orange

push

Nối phần tử vào cuối mảng:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let fruits = ["Apple", "Orange"];

fruits.push("Pear");

alert( fruits ); // Apple, Orange, Pear

Khi gọi fruits.push(...)bằng fruits[fruits.length] = ....

Các phương thức hoạt động với phần đầu của mảng:shift

Trích xuất phần tử đầu tiên của mảng và trả về nó:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let fruits = ["Apple", "Orange", "Pear"];

alert( fruits.shift() ); // remove Apple and alert it

alert( fruits ); // Orange, Pear

unshift

Thêm phần tử vào đầu mảng:

let fruits = ["Orange", "Pear"];

fruits.unshift('Apple');

alert( fruits ); // Apple, Orange, Pear

Phương thức pushunshiftcó thể thêm nhiều phần tử cùng một lúc:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let fruits = ["Apple"];

fruits.push("Orange", "Peach");
fruits.unshift("Pineapple", "Lemon");

// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
alert( fruits );

Nội bộ

Một mảng là một loại đối tượng đặc biệt. Dấu ngoặc vuông được sử dụng để truy cập một thuộc tính arr[0]thực sự đến từ cú pháp đối tượng. Điều đó về cơ bản giống như obj[key].

Chúng mở rộng các đối tượng cung cấp các phương thức đặc biệt để làm việc với các bộ sưu tập dữ liệu theo thứ tự và cả thuộc lengthtính. Nhưng cốt lõi nó vẫn là một đối tượng.




Hãy nhớ rằng, chỉ có 7 loại cơ bản trong JavaScript. Mảng là một đối tượng và do đó hoạt động như một đối tượng.

Ví dụ, nó được sao chép bằng cách tham chiếu:

let fruits = ["Banana"]

let arr = fruits; // copy by reference (two variables reference the same array)

alert( arr === fruits ); // true

arr.push("Pear"); // modify the array by reference

alert( fruits ); // Banana, Pear - 2 items now

Nhưng điều làm cho mảng thực sự đặc biệt là đại diện nội bộ của họ. Công cụ cố gắng lưu trữ các phần tử của nó trong vùng nhớ liền kề nhau, giống như được mô tả trên hình minh họa trong chương này, và cũng có những tối ưu hóa khác, để làm cho mảng hoạt động rất nhanh.

Nhưng tất cả đều vỡ nếu chúng ta bỏ làm việc với một mảng như với một bộ sưu tập có thứ tự và bắt đầu làm việc với nó như thể nó là một đối tượng thông thường.

Ví dụ, về mặt kỹ thuật, chúng tôi có thể làm điều này:

let fruits = []; // make an array

fruits[99999] = 5; // assign a property with the index far greater than its length

fruits.age = 25; // create a property with an arbitrary name

Điều đó là có thể, bởi vì mảng là các đối tượng. Chúng ta có thể thêm bất kỳ thuộc tính nào cho chúng.

Nhưng động cơ sẽ thấy rằng chúng ta đang làm việc với mảng như với một đối tượng thông thường. Tối ưu hóa cụ thể mảng không phù hợp cho các trường hợp như vậy và sẽ bị tắt, lợi ích của chúng biến mất.

Các cách để sử dụng sai một mảng:

  • Thêm một thuộc tính không phải là số như thế arr.test = 5.
  • Tạo mảng, như: thêm arr[0]và sau đó arr[1000](và không có gì giữa chúng).
  • Điền vào các mảng theo thứ tự ngược lại, như arr[1000], arr[999]và như vậy.

Hãy nghĩ về các mảng như các cấu trúc đặc biệt để làm việc với dữ liệu được sắp xếp. Họ cung cấp các phương pháp đặc biệt cho điều đó. Mảng được điều chỉnh cẩn thận bên trong các công cụ JavaScript để làm việc với dữ liệu được sắp xếp liền kề, vui lòng sử dụng chúng theo cách này. Và nếu bạn cần các khóa tùy ý, rất có thể bạn thực sự cần một đối tượng thông thường {}.

Hiệu suất

Phương pháp push/popchạy nhanh, trong khi shift/unshiftchậm.




Tại sao nó nhanh hơn để làm việc với phần cuối của một mảng so với phần đầu của nó? Hãy xem điều gì xảy ra trong quá trình thực thi:

fruits.shift(); // take 1 element from the start

Nó không chỉ để lấy và loại bỏ phần tử với số 0. Các yếu tố khác cũng cần phải được đánh số lại.

Các shifthoạt động phải làm 3 việc:

  1. Loại bỏ phần tử với chỉ mục 0.
  2. Di chuyển tất cả các yếu tố sang trái, đánh số lại chúng từ chỉ mục 1sang 0, từ 2đến 1và vv.
  3. Cập nhật thuộc tính length.

Càng nhiều phần tử trong mảng, thời gian di chuyển chúng càng nhiều, hoạt động trong bộ nhớ càng nhiều.

Điều tương tự xảy ra với unshift: để thêm một phần tử vào đầu mảng, trước tiên chúng ta cần di chuyển các phần tử hiện có sang bên phải, tăng chỉ mục của chúng.

Và với push/popcái gì? Họ không cần phải di chuyển bất cứ điều gì. Để trích xuất một phần tử từ cuối, popphương thức làm sạch chỉ mục và rút ngắn length.

Các hành động cho pop:

fruits.pop(); // take 1 element from the end

Các phương thứcpop không cần phải di chuyển bất cứ điều gì, bởi vì các yếu tố khác giữ chỉ số của họ. Đó là lý do tại sao nó rất nhanh.

Điều tương tự với pushphương pháp.

Vòng lặp

Một trong những cách lâu đời nhất để xoay vòng các mục trong mảng là vòng lặp for trên các chỉ mục:




/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let arr = ["Apple", "Orange", "Pear"];

for (let i = 0; i < arr.length; i++) {
  alert( arr[i] );
}

Nhưng đối với mảng có một dạng vòng lặp khác , for..of:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let fruits = ["Apple", "Orange", "Plum"];

// iterates over array elements
for (let fruit of fruits) {
  alert( fruit );
}

Các for..ofkhông cho truy cập vào các số của phần tử hiện tại, chỉ cần giá trị của nó, nhưng trong nhiều trường hợp như vậy là đủ. Và nó ngắn hơn.

Về mặt kỹ thuật, vì mảng là đối tượng, nên cũng có thể sử dụng for..in:

let arr = ["Apple", "Orange", "Pear"];

for (let key in arr) {
  alert( arr[key] ); // Apple, Orange, Pear
}

Nhưng đó thực sự là một ý tưởng tồi. Có những vấn đề tiềm ẩn với nó:

  1. Vòng for..inlặp trên tất cả các thuộc tính, không chỉ các số. Có cái gọi là các đối tượng giống như mảng trong các trình duyệt và trong các môi trường khác, trông giống như các mảng . Đó là, chúng có lengthvà lập chỉ mục các thuộc tính, nhưng chúng cũng có thể có các thuộc tính và phương thức không phải mà chúng ta thường không cần. Các vòng lặp for..in sẽ liệt kê chúng. Vì vậy, nếu chúng ta cần phải làm việc với các đối tượng giống như mảng, thì các thuộc tính này có thể trở thành một vấn đề.
  2. Các for..invòng lặp được tối ưu hóa cho các đối tượng chung chung, không mảng, và do đó là 10-100 lần chậm hơn. Tất nhiên, nó vẫn rất nhanh. Việc tăng tốc chỉ có thể là vấn đề trong các nút cổ chai. Nhưng chúng ta vẫn nên nhận thức được sự khác biệt.

Nói chung, chúng ta không nên sử dụng for..incho mảng.

Về chiều dài

Các length tự động cập nhật khi chúng ta thay đổi mảng. Nói chính xác, nó thực sự không phải là số lượng giá trị trong mảng, mà là chỉ số số lớn nhất cộng với một.

Chẳng hạn, một yếu tố duy nhất có chỉ số lớn cho độ dài lớn:

let fruits = [];
fruits[123] = "Apple";

alert( fruits.length ); // 124

Lưu ý rằng chúng ta thường không sử dụng mảng như thế.

Một điều thú vị khác về thuộc tínhlength là nó có thể ghi được.

Nếu chúng ta tăng nó bằng tay, không có gì thú vị xảy ra. Nhưng nếu chúng ta giảm nó, mảng bị cắt ngắn. Quá trình là không thể đảo ngược, đây là ví dụ:




let arr = [1, 2, 3, 4, 5];

arr.length = 2; // truncate to 2 elements
alert( arr ); // [1, 2]

arr.length = 5; // return length back
alert( arr[3] ); // undefined: the values do not return

Vì vậy, cách đơn giản nhất để xóa mảng là : arr.length = 0;.

new Array()

Có thêm một cú pháp để tạo một mảng:

let arr = new Array("Apple", "Pear", "etc");

Nó hiếm khi được sử dụng, bởi vì dấu ngoặc vuông []ngắn hơn. Ngoài ra có một tính năng khó khăn với nó.

Nếu new Arrayđược gọi với một đối số duy nhất, thì nó tạo ra một mảng không có các mục, nhưng với độ dài cho trước .

Hãy xem người ta có thể tự bắn vào chân mình như thế nào:

let arr = new Array(2); // will it create an array of [2] ?

alert( arr[0] ); // undefined! no elements.

alert( arr.length ); // length 2

Trong đoạn code trên, new Array(number)có các yếu tố undefined.

Để tránh những bất ngờ như vậy, chúng ta thường sử dụng dấu ngoặc vuông, trừ khi chúng ta thực sự biết những gì chúng ta đang làm.

Mảng nhiều chiều

Mảng có thể có các mục cũng là mảng. Chúng ta có thể sử dụng nó cho các mảng nhiều chiều, ví dụ để lưu trữ ma trận:

let matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

alert( matrix[1][1] ); // 5, the central element

Mảng có cách thực hiện riêng với phương thức toString trả về danh sách các phần tử được phân tách bằng dấu phẩy.

Ví dụ:




let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true

Ngoài ra, hãy thử điều này:

alert( [] + 1 ); // "1"
alert( [1] + 1 ); // "11"
alert( [1,2] + 1 ); // "1,21"

Mảng không có Symbol.toPrimitive, không khả thi valueOf, chúng chỉ thực hiện toStringchuyển đổi, vì vậy ở đây []trở thành một chuỗi rỗng, [1]trở thành "1"[1,2]trở thành "1,2".

Khi toán tử "+"nhị phân cộng thêm một cái gì đó vào một chuỗi, nó cũng chuyển đổi nó thành một chuỗi, vì vậy bước tiếp theo sẽ như sau:

alert( "" + 1 ); // "1"
alert( "1" + 1 ); // "11"
alert( "1,2" + 1 ); // "1,21"

Tóm lược

Mảng là một loại đối tượng đặc biệt, phù hợp để lưu trữ và quản lý các mục dữ liệu theo thứ tự.

  • Khai báo:
// square brackets (usual) 
let arr = [item1, item2...]; // new Array (exceptionally rare) 
let arr = new Array(item1, item2...);
  • Gọi new Array(number) để tạo một mảng với độ dài cho trước, nhưng không có phần tử.
  • Các length là chiều dài mảng hoặc, được tính bằng chỉ số số cuối cùng của nó cộng với một. Nó được điều chỉnh tự động bằng các phương thức mảng.
  • Nếu chúng ta rút ngắn lengththủ công, mảng bị cắt ngắn.

Chúng ta có thể sử dụng một mảng như một deque với các hoạt động sau:

  • push(...items)thêm itemsvào cuối
  • pop() loại bỏ phần tử từ cuối và trả về nó.
  • shift() loại bỏ phần tử từ đầu và trả về nó.
  • unshift(...items)thêm itemsvào đầu

Để lặp qua các phần tử của mảng:

  • for (let i=0; i<arr.length; i++) – hoạt động nhanh nhất, tương thích trình duyệt cũ.
  • for (let item of arr) – cú pháp hiện đại chỉ lặp các mục(phần từ của mảng),
  • for (let i in arr) – không bao giờ sử dụng.

Chúng ta sẽ quay trở lại mảng và nghiên cứu thêm các phương thức để thêm, xóa, trích xuất các phần tử và sắp xếp các mảng trong các phương thức Array sau này.