Trong quá trình phát triển ứng dụng Web hiện đại, việc xử lý cấu trúc dữ liệu là kỹ năng cốt lõi của mọi lập trình viên. Thao tác thêm phần tử vào mảng trong javascript không chỉ đơn thuần là gọi một hàm có sẵn, mà còn liên quan mật thiết đến việc quản lý bộ nhớ, tối ưu hóa hiệu suất (Big O) và đảm bảo tính bất biến (immutability) của dữ liệu trong các Framework như React hay Vue.

Cơ chế quản lý bộ nhớ của mảng trong JavaScript

Trước khi đi sâu vào code, chúng ta cần hiểu cách Engine JavaScript (như V8 của Chrome) quản lý mảng. Khác với C++ nơi mảng có kích thước cố định, mảng trong JavaScript là các “Dynamic Arrays”.

Khi bạn tiến hành thêm phần tử vào mảng trong javascript, nếu kích thước vượt quá bộ nhớ đệm (capacity), Engine sẽ cấp phát một vùng nhớ mới lớn hơn (thường là gấp đôi), sau đó sao chép các phần tử cũ sang. Hiểu được điều này sẽ giúp bạn tránh được các lỗi về hiệu năng khi xử lý tập dữ liệu lớn (Big Data).

1. Phương thức push() – Thêm vào cuối mảng (O(1))

Phương thức push() là cách phổ biến nhất để thêm một hoặc nhiều phần tử vào cuối mảng. Phương thức này thay đổi trực tiếp mảng gốc (mutating) và trả về độ dài mới của mảng sau khi thêm.

Phiên bản: ECMAScript 1 (ES1+)
Độ phức tạp: O(1) trung bình (Amortized constant time).

// Ngôn ngữ: JavaScript (Node.js v18+)
const fruits = ['🍎', '🍌'];

// Thêm một phần tử
const newLength = fruits.push('🍇'); 

// Thêm nhiều phần tử cùng lúc
fruits.push('🍊', '🥭');

console.log(fruits); 
// Output: ["🍎", "🍌", "🍇", "🍊", "🥭"]
console.log(newLength); 
// Output: 3

/
  Kinh nghiệm thực tế:
  'push()' cực kỳ nhanh vì nó không phải thay đổi index của các phần tử cũ.
  Tuy nhiên, trong React state, đừng dùng push() trực tiếp vì nó gây side effect.
 /

Minh họa phương thức push thêm phần tử vào cuốiMinh họa phương thức push thêm phần tử vào cuối

2. Phương thức unshift() – Thêm vào đầu mảng (O(n))

Ngược lại với push(), unshift() thêm phần tử vào vị trí index 0. Đây là thao tác “đắt đỏ” về mặt hiệu năng vì JavaScript phải cập nhật lại chỉ số (index) cho tất cả các phần tử hiện có.

Độ phức tạp: O(n) – Tỷ lệ thuận với số lượng phần tử trong mảng.

// Ngôn ngữ: JavaScript ES6+
let queue = ['User2', 'User3'];

// Thêm phần tử ưu tiên vào đầu danh sách
queue.unshift('User1');

console.log(queue); 
// Output: ["User1", "User2", "User3"]

/
  Pitfall: Nếu mảng có 1 triệu phần tử, việc dùng unshift() 
  sẽ khiến CPU phải thực hiện 1 triệu phép gán lại index. 
  Hãy cân nhắc sử dụng cấu trúc Deque (Double-ended queue) nếu cần thêm vào đầu thường xuyên.
 /

3. Sử dụng splice() để chèn vào vị trí bất kỳ

Nếu bạn cần thêm phần tử vào mảng trong javascript tại một vị trí cụ thể (ví dụ: chèn giữa), splice() là công cụ vạn năng. Nó có thể vừa xóa, vừa thêm cùng lúc.

Cấu pháp: array.splice(index, deleteCount, item1, item2, ...)

// Ngôn ngữ: JavaScript
const colors = ['Red', 'Blue', 'Green'];

// Chèn 'Yellow' vào vị trí index 1 mà không xóa phần tử nào
colors.splice(1, 0, 'Yellow');

console.log(colors);
// Output: ["Red", "Yellow", "Blue", "Green"]

// Edge case: Chèn ở vị trí index lớn hơn độ dài mảng
colors.splice(100, 0, 'Purple'); 
// Sẽ tự động thêm vào cuối mảng nếu index > length

Cách dùng splice để chèn phần tử vào giữa mảngCách dùng splice để chèn phần tử vào giữa mảng

4. Kỹ thuật Spread Operator (…) – Chuẩn ES6

Trong lập trình hàm (Functional Programming) và các thư viện như Redux, việc giữ nguyên mảng gốc (Immutability) là bắt buộc. Spread Operator giúp tạo ra một mảng mới chứa các phần tử cũ và phần tử mới.

Độ phức tạp: O(n) (Do phải tạo mảng mới và copy toàn bộ nội dung).

// Ngôn ngữ: ES6+
const original = [1, 2, 3];

// Thêm vào cuối mà không mutate mảng gốc
const addedAtEnd = [...original, 4];

// Thêm vào đầu
const addedAtStart = [0, ...original];

// Chèn vào giữa (kết hợp slice)
const midIndex = 1;
const insertedMid = [
    ...original.slice(0, midIndex),
    1.5,
    ...original.slice(midIndex)
];

console.log(original); // Vẫn là [1, 2, 3]
console.log(insertedMid); // [1, 1.5, 2, 3]

Sử dụng Spread Operator để thêm phần tử an toànSử dụng Spread Operator để thêm phần tử an toàn

5. Phương thức concat() – Kết nối dữ liệu

concat() tương tự như Spread Operator nhưng có từ thời kỳ đầu của JavaScript. Nó không làm thay đổi các mảng ban đầu mà trả về một mảng mới là sự kết hợp của chúng.

// Ngôn ngữ: JavaScript
const alpha = ['a', 'b'];
const numeric = [1, 2];

// Kết hợp mảng với giá trị đơn lẻ
const combined = alpha.concat(3, numeric);

console.log(combined); 
// Output: ["a", "b", 3, 1, 2]

6. Gán qua thuộc tính length (Thủ thuật High-Performance)

Một cách ít người biết nhưng lại rất nhanh để thêm phần tử vào cuối mảng là sử dụng thuộc tính length làm index. Cách này tránh việc phải gọi phương thức (function call overhead).

const arr = [10, 20, 30];

// Thêm phần tử bằng index
arr[arr.length] = 40;

console.log(arr); // [10, 20, 30, 40]

Dùng thuộc tính length để thêm phần tửDùng thuộc tính length để thêm phần tử

So sánh hiệu năng giữa các phương pháp

Khi làm việc với mảng cực lớn (trên 100.000 phần tử), sự khác biệt giữa các phương pháp thêm phần tử vào mảng trong javascript trở nên rất rõ rệt.

Phương pháp Vị trí chèn Độ phức tạp Mutability Ghi chú
push() Cuối O(1) Nhanh nhất cho mảng lớn
unshift() Đầu O(n) Rất chậm khi mảng phình to
splice() Bất kỳ O(n) Linh hoạt nhất
concat() Cuối/Bất kỳ O(n) Không Tốn bộ nhớ hơn vì tạo mảng mới
Spread Bất kỳ O(n) Không Cú pháp sạch, dùng nhiều trong React

Xử lý các lỗi thường gặp (Pitfalls)

Trong sự nghiệp lập trình, tôi đã gặp không ít trường hợp hệ thống treo chỉ vì dùng sai cách thêm phần tử. Dưới đây là 2 sai lầm kinh điển:

Sai lầm 1: Thêm phần tử tạo ra “Holey Arrays” (Mảng có lỗ)

Khi bạn gán giá trị tại một index cách quá xa length hiện tại, JavaScript sẽ tạo ra một “sparse array” (mảng thưa).

const sparse = [1, 2];
sparse[100] = 99; 
// Mảng giờ có 101 phần tử, nhưng 98 phần tử ở giữa là 'empty'
// Điều này khiến Engine V8 không thể tối ưu mảng dưới dạng mảng đóng gói (Packed)

Sai lầm 2: Spread Operator với mảng quá lớn

Sử dụng [...array] cho mảng có hàng triệu phần tử có thể gây ra lỗi RangeError: Maximum call stack size exceeded ở một số môi trường cũ hoặc lỗi tràn bộ nhớ (Heap out of memory) do mỗi lần spread là một lần tạo bản sao mới.

Ứng dụng thực tế: Xây dựng hàm chèn dữ liệu tối ưu

Trong các dự án thực tế, bạn nên viết một hàm Utility để xử lý việc chèn dữ liệu theo nhu cầu cụ thể của project, ví dụ như hàm chèn vào mảng đã được sắp xếp (Sorted Array).

/
  Chèn phần tử vào mảng đã sắp xếp mà vẫn giữ nguyên thứ tự (Binary Search Insertion)
  @param {Array} arr - Mảng mục tiêu
  @param {} item - Phần tử cần thêm
  @returns {number} - Index mới của phần tử
 /
function insertSorted(arr, item) {
    let low = 0;
    let high = arr.length;

    while (low < high) {
        let mid = (low + high) >>> 1;
        if (arr[mid] < item) low = mid + 1;
        else high = mid;
    }
    arr.splice(low, 0, item);
    return low;
}

const numbers = [10, 20, 30, 40];
insertSorted(numbers, 25);
console.log(numbers); // [10, 20, 25, 30, 40]

Các nguồn tham khảo uy tín

Để tìm hiểu sâu hơn về các đặc tả kỹ thuật và cách tối ưu RAM cho mảng, bạn có thể tham khảo các tài liệu chính thống sau:

  1. MDN Web Docs: Array.prototype.push()
  2. ECMAScript® 2023 Language Specification – Section 23.1

Việc nắm vững các kỹ thuật thêm phần tử vào mảng trong javascript giúp bạn viết code sạch hơn, tránh các lỗi phổ biến về hiệu năng và chuẩn bị tốt cho các thư viện phức tạp. Hãy luôn cân nhắc giữa tốc độ (performance) và tính dễ đọc của code (readability) để đưa ra lựa chọn phù hợp nhất cho dự án của mình. Gợi ý tiếp theo, bạn có thể tìm hiểu về cách xóa phần tử khỏi mảng để hoàn thiện kỹ năng CRUD trên cấu trúc dữ liệu mảng.

Cập nhật lần cuối 01/03/2026 by Hiếu IT

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *