Destructuring là gì? Nó có vai trò gì trong việc nâng tầm kỹ năng Javascript của bạn? Trong bài viết này, VietnamWorks inTECH sẽ đưa bạn khám phá những ưu điểm tuyệt vời mà Destructuring mang lại, đồng thời hướng dẫn bạn cách sử dụng thành thạo cú pháp "xịn xò" này để viết code Javascript đẹp hơn, chất hơn.

1. Destructuring là gì?

Destructuring là một tính năng cú pháp đặc biệt thú vị trong JavaScript, cho phép chúng ta trích xuất các giá trị từ mảng, đối tượng hoặc các cấu trúc lặp khác và gán chúng cho các biến.

Đây là cách viết tắt để truy cập các thuộc tính hoặc phần tử của một cấu trúc dữ liệu mà không cần phải sử dụng ký hiệu dấu chấm hoặc chỉ số mảng.

2. Ưu điểm mà Destructuring mang lại

Việc sử dụng destructuring sẽ mang lại rất nhiều lợi ích trong quá trình viết code của bạn:

  • Cải thiện khả năng đọc: Destructuring đơn giản hóa code bằng cách giảm nhu cầu gán biến phức tạp và sử dụng ký hiệu chấm (dot notation).

  • Ít code rập khuôn hơn: Bạn có thể trích xuất giá trị trực tiếp từ các cấu trúc dữ liệu mà không cần tạo các biến trung gian.

  • Code gọn hơn: Destructuring có thể giảm số dòng code cần thiết để đạt được cùng một kết quả.

  • Linh hoạt: Bạn có thể destructure các cấu trúc dữ liệu thuộc bất kỳ loại nào (đối tượng, mảng, iterable), làm cho nó trở thành một công cụ đa năng trong bộ công cụ JavaScript của bạn.

Destructuring hiệu quả cho phép chúng ta viết code biểu cảm hơn, dễ bảo trì, dễ hiểu và dễ debug.

Ví dụ:

const person = { name: 'John', age: 30 };
const { name, age } = person;
console.log(name); // "John"
console.log(age); // 30

Ở đây chúng ta đã destructure một đối tượng person với hai thuộc tính: name và age.

Khi destructure một đối tượng JavaScript, các giá trị chúng ta trích xuất phải khớp chính xác với các key trong object đó. Bạn không thể đặt userName thay thế cho name trong dòng lệnh const { name, age } = person; nghĩa là - const { userName, age } = person; sẽ không hoạt động.

Tuy nhiên, có thể sử dụng alias (đổi tên) khi destructure một đối tượng. Ví dụ:

const person = { name: 'John', age: 30 };
const { name:userName, age:userAge } = person;
console.log(userName); // "John"
console.log(userAge); // 30

Rất có thể bạn đã thấy việc destructure một đối tượng lần đầu tiên khi bạn đang nhập một module. Ví dụ khi nhập hàm exec - 

import { exec } from "node:child_process"; // ES Module syntax
 
const { exec } = require("child_process"); // commonJS syntax

Tương tự như vậy chúng ta cũng có thể phân tích cấu trúc mảng -

const numbers = [4, 5, 6];
const [x, y, z] = numbers;
console.log(x); // 4
console.log(y); // 5
console.log(z); // 6

Ở đây, khi destructure mảng, bạn không cần sử dụng việc đặt bí danh để gán bất kỳ phần tử nào cho một tên biến tùy chỉnh. Bởi vì các phần tử của mảng chỉ đơn giản là các giá trị, chúng không bị ràng buộc với các key.

3. Giá trị mặc định

Destructuring cho phép bạn gán giá trị mặc định cho các biến nếu thuộc tính không tồn tại trong đối tượng.

const person = { name: 'John' };
const { name = 'Anonymous', age } = person; // age will be undefined
console.log(name); // "John"
console.log(age); // undefined

Ở đây, giá trị chuỗi 'John' không bị thay thế bởi giá trị 'Anonymous' trong biến name vì nó đã tồn tại trong đối tượng.

Trong khi đó -

const person = { name: 'John' };
const { name, age = 30 } = person; // age defaults to 30 if not present
console.log(name); // "John"
console.log(age); // 30

4. Cú pháp spread

Cú pháp spread hoặc operator (...) có thể được sử dụng với destructuring để nắm bắt các phần tử còn lại của một mảng hoặc các thuộc tính của một đối tượng vào một biến mới.

Cú pháp spread với Mảng - 

const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first);  // 1
console.log(second); // 2
console.log(rest);    // [3, 4, 5] (remaining elements)

Cú pháp spread với Đối tượng- 

const person = { name: 'John', age: 30, city: 'New York' };
const { name, ...info } = person;
console.log(name);  // "John"
console.log(info);   // { age: 30, city: "New York"} (remaining properties)

5. Destructuring Lồng Nhau

Destructuring có thể được lồng nhau để trích xuất các giá trị từ các đối tượng hoặc mảng lồng nhau sâu.

const data = {
  user: {
    name: 'Alicia',
    origin: 'Romania',
    eyes: 'blue',
    address: {
      city: 'London',
    }
  }
};

const { user: { name, address: { city } } } = data;
console.log(name);  // "Alicia"
console.log(city);   // "London"

6. Destructuring trong danh sách tham số của hàm

Giả sử chúng ta có một đối tượng JavaScript có tên credentials-

const credentials = {
  name: 'Debajyati',
  age: 20,
  address: {
    city: 'Kolkata',
    state: 'West Bengal',
    country: 'India'
  },
  phone: '',
  email: '',
  hobbies: ['reading', 'listening to music', 'coding', 'watching Anime'],
  skills: {
    programming: true,
    blogging: true,
    singing: false
  }
}

Và một hàm có tên là showCredentials nhận duy nhất 1 đối số là một đối tượng và xuất ra một chuỗi dựa trên một số thuộc tính của đối tượng. Chúng ta có thể viết định nghĩa hàm theo cách này - 

function showCredential(obj) {
  const hasSkill = (skill) => obj.skills[skill];
  console.log(
    `${obj.name} is ${obj.age} years old.\n Lives in ${obj.address.city}, ${obj.address.country}.\n`,
    `He has the following hobbies: ${obj.hobbies.join(", ")}`,
  );
  if (hasSkill("programming")) {
    console.log(`He is a programmer.`);
  }
  if (hasSkill("singing")) {
    console.log(`He is a singer.`);
  }
  if (hasSkill("blogging")) {
    console.log(`He is also a tech blogger.`);
  }
}

Gọi nó với - 

showCredential(credentials);

Output - 

Debajyati is 20 years old.
 Lives in Kolkata, India.
 He has the following hobbies: reading, listening to music, coding, watch
ing Anime
He is a programmer.
He is also a tech blogger.

Thay vào đó, chúng ta có thể destructuring đối số đối tượng trong danh sách tham số khi định nghĩa hàm. Như thế này - 

function showCredential({ name, age, address: { city, country}, hobbies, skills }) {
  const hasSkill = (skill) => skills[skill];
  console.log(
    `${name} is ${age} years old.\n Lives in ${city}, ${country}.\n`,
    `He has the following hobbies: ${hobbies.join(", ")}`,
  );
  if (hasSkill("programming")) {
    console.log(`He is a programmer.`);
  }
  if (hasSkill("singing")) {
    console.log(`He is a singer.`);
  }
  if (hasSkill("blogging")) {
    console.log(`He is also a tech blogger.`);
  }
}

Output sẽ tương tự như trên

Lưu ý:  Destructuring không làm tăng số lượng đối số trong danh sách tham số của hàm. Nó chỉ là một cách khác để trích xuất các giá trị từ đối tượng được truyền vào tham số đó.  Về bản chất, hàm vẫn chỉ nhận một đối tượng duy nhất làm đầu vào.

7. Destructuring chuỗi

Tương tự như cách destructuring chuỗi, chúng ta cũng có thể giải nén các chuỗi như các phần tử của mảng

const fruit = 'grape';
const [first, second, ...rest] = fruit;
const animal = rest.join('');
console.log(animal); // ape

Lưu ý: Khi bạn sử dụng toán tử spread (...) để nắm bắt các ký tự còn lại từ một chuỗi, bạn không nhận được một chuỗi mà nhận được một mảng các ký tự đó.

8. Một số ví dụ ứng dụng tiện dụng của việc Destructuring

8.1. Destructuring để hoán đổi mà không cần biến thứ ba:

JavaScript truyền thống yêu cầu một biến tạm thời để hoán đổi giá trị của hai biến. Destructuring cung cấp một cách ngắn gọn và dễ đọc hơn để làm được điều này.

  • Trước khi Destructuring:

let a = 10;
let b = 20;

let temp = a;
a = b;
b = temp;
console.log(a, b); // Output: 20 10
  • Sau khi Destructuring:

let a = 10;
let b = 20;

[a, b] = [b, a];

console.log(a, b); // Output: 20 10

8.2. Destructuring các giá trị trả về của hàm

Các hàm có thể trả về nhiều giá trị dưới dạng mảng hoặc đối tượng. Destructuring cho phép bạn giải nén những giá trị trả về này vào các biến riêng lẻ, cải thiện độ rõ ràng của mã. Giả sử bạn có một hàm lấy dữ liệu từ một API và trả về một đối tượng phản hồi:

function getUserUpdates(id) {
  // Simulating some API call with a GET request
  return {
    data: {
      player: response.group.names[id],
      brain: "rotting",
      powerLevel: Number(response.group.power[id]),
      useAsDecoy: true,
    },
    statusCode: Number(response.status),
  };
}

Trong bối cảnh xây dựng API hoặc xử lý các phản hồi từ máy chủ, nó mang lại những lợi thế rõ rệt, nâng cao chất lượng và khả năng duy trì mã.

Việc truy cập từng thuộc tính sẽ trở nên dễ dàng, vì bạn có thể trực tiếp trích xuất các thuộc tính cần thiết từ giá trị trả về của hàm vào các biến riêng lẻ trong chính cuộc gọi hàm.

const {
  data: {player, useAsDecoy, powerLevel},
  statusCode,
} = getUserUpdates(1);

Bất cứ khi nào một hàm trả về một đối tượng và bạn quan tâm đến các giá trị thuộc tính cụ thể, hãy áp dụng destructuring ngay lập tức. Nếu bạn vẫn nghĩ rằng việc destructuring trong các giá trị trả về không phải là ý tưởng hay, hai lợi thế sau có thể thuyết phục bạn:

  • (A) Mô Hình Tư Duy Đơn Giản: Destructuring đơn giản hóa quá trình suy nghĩ cần thiết để hiểu luồng dữ liệu cho các nhà phát triển sẽ sử dụng hàm của bạn. Thay vì phải ghi nhớ các chuỗi truy cập thuộc tính phức tạp, các lập trình viên có thể tập trung vào ý nghĩa được truyền tải bởi các tên biến được sử dụng trong mẫu destructuring. Điều này giảm tải nhận thức và thúc đẩy sự hiểu biết tốt hơn về code.

  • (B) Giảm Bớt code boilerplate Cho Các Đối Tượng Trả Về Phức Tạp: Khi các hàm trả về các đối tượng có nhiều thuộc tính hoặc các thuộc tính lồng nhau, destructuring giúp giảm đáng kể code boilerplate cần thiết để truy cập từng thuộc tính riêng lẻ. Điều này dẫn đến một codebase gọn hơn và ít lộn xộn hơn, cải thiện chất lượng code tổng thể.

8.3. Destructuring có điều kiện

Destructuring có thể kết hợp với các câu lệnh điều kiện để xử lý các kịch bản khác nhau dựa trên cấu trúc của một đối tượng. Nếu bạn có một hàm nhận vào một đối tượng với các thuộc tính tùy chọn, bạn có thể làm như sau:

function greetUser(user) {
  const { name = "Anonymous" } = user || {}; // Destructuring with default value

  console.log(`Hello, ${name}!`);
}

greetUser({ name: "Bob" });       // Output: "Hello, Bob!"
greetUser({});                    // Output: "Hello, Anonymous!" (no name property)
greetUser(undefined);           // Output: "Hello, Anonymous!" (function receives no argument)

Lời kết

VietnamWorks inTECH hy vọng qua bài viết này bạn đã hiểu rõ hơn về cách sử dụng destructuring để nâng cao chất lượng mã nguồn của mình. Hãy tận dụng tối đa những lợi ích mà cú pháp này mang lại để trở thành một lập trình viên JavaScript xuất sắc hơn. Nếu bạn thấy bài viết hữu ích, đừng ngần ngại chia sẻ với bạn bè của mình nhé!

VietnamWorks inTECH