+8

JavaScript Nâng Cao - Kỳ 12

Có một câu nói vui là: Trên đời chỉ có thứ nhiều người chửi và thứ không ai thèm dùng.

Javascript là một ví dụ điển hình, nó có một số điểm thú vị nhưng cũng khiến chúng ta phải đau đầu. Lý thuyết thì dễ hiểu, nhưng khi thực hành là cả một vấn đề. Vậy nên, mình sẽ cùng các bạn đi sâu vào từng ví dụ cụ thể và phân tích, mổ xẻ nó để hiểu hơn về Javascript nhé.

Series này có thể sẽ khá dài mình không biết sẽ có bao nhiêu Kỳ tuy nhiên để tiện cho các bạn nào không đọc các bài trước đó của mình về JS thì trong loạt bài này mình sẽ giải thích lại toàn bộ. Các lý thuyết trong loạt bài này mình cũng có thể sẽ giải thích lại nhiều lần (tùy hứng) để các bạn có thể năm rõ nó hơn nhé.

Ok vào bài thôi nào... GÉT GÔ 🚀

Nếu có bất kỳ câu hỏi nào đừng ngại hãy bình luận dưới phần comment nhé. Hoặc chỉ cần để lại một comment chào mình là đã giúp mình có thêm động lực hoàn thành series này. Cảm ơn các bạn rất nhiều. 🤗

1. Set và các giá trị duy nhất

Output của đoạn code bên dưới là gì:

const set = new Set([1, 1, 2, 3, 4]);

console.log(set);
  • A: [1, 1, 2, 3, 4]
  • B: [1, 2, 3, 4]
  • C: {1, 1, 2, 3, 4}
  • D: {1, 2, 3, 4}
Đáp án của câu hỏi này là 
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
Đáp án: D

Cùng mình đi tìm hiểu tại sao kết quả lại là như vậy nhé ❓️

1.1. Set trong JavaScript

Set là một cấu trúc dữ liệu trong JavaScript được giới thiệu từ ES6. Nó cho phép bạn lưu trữ các giá trị duy nhất của bất kỳ kiểu dữ liệu nào, bao gồm cả các giá trị nguyên thủy và các tham chiếu đối tượng.

1.2. Đặc điểm của Set

  1. Giá trị duy nhất: Mỗi giá trị trong Set chỉ có thể xuất hiện một lần. Nếu bạn thêm một giá trị đã tồn tại, nó sẽ bị bỏ qua.

  2. Thứ tự chèn: Set giữ nguyên thứ tự chèn của các phần tử. Điều này có nghĩa là khi bạn lặp qua một Set, các phần tử sẽ được trả về theo thứ tự chúng được chèn vào.

  3. Không có chỉ mục: Không giống như mảng, Set không có chỉ mục. Bạn không thể truy cập các phần tử bằng chỉ mục.

1.3. Phân tích đoạn code

Trong đoạn code của chúng ta:

const set = new Set([1, 1, 2, 3, 4]);

Chúng ta đang tạo một Set mới và khởi tạo nó với một mảng [1, 1, 2, 3, 4]. Set sẽ tự động loại bỏ các giá trị trùng lặp. Trong trường hợp này, giá trị 1 xuất hiện hai lần trong mảng ban đầu, nhưng Set chỉ giữ lại một lần.

Khi chúng ta console.log(set), kết quả sẽ là {1, 2, 3, 4}. Đây là cách JavaScript hiển thị một Set trong console. Nó sử dụng dấu ngoặc nhọn {} thay vì dấu ngoặc vuông [] như mảng.

1.4. Ví dụ minh họa

Để hiểu rõ hơn, hãy xem xét một ví dụ khác:

const fruits = new Set(['apple', 'banana', 'apple', 'orange', 'banana']);
console.log(fruits); // Output: Set(3) { 'apple', 'banana', 'orange' }

// Thêm phần tử vào Set
fruits.add('mango');
fruits.add('apple'); // 'apple' đã tồn tại nên sẽ không được thêm vào

console.log(fruits); // Output: Set(4) { 'apple', 'banana', 'orange', 'mango' }

Trong ví dụ này, chúng ta thấy rằng Set tự động loại bỏ các giá trị trùng lặp ('apple' và 'banana'). Khi chúng ta thêm 'mango', nó được thêm vào Set. Tuy nhiên, khi chúng ta cố gắng thêm 'apple' lần nữa, Set vẫn giữ nguyên vì 'apple' đã tồn tại.

1.5. Tóm lại

Set là một cấu trúc dữ liệu hữu ích trong JavaScript khi bạn cần lưu trữ một tập hợp các giá trị duy nhất. Nó tự động xử lý việc loại bỏ các giá trị trùng lặp, giúp code của bạn gọn gàng và hiệu quả hơn trong nhiều trường hợp.

2. Import và Read-only Module

Output của đoạn code bên dưới là gì:

// counter.js
let counter = 10;
export default counter;
// index.js
import myCounter from "./counter";

myCounter += 1;

console.log(myCounter);
  • A: 10
  • B: 11
  • C: Error
  • D: NaN
Đáp án của câu hỏi này là 
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
Đáp án: C

Cùng mình đi tìm hiểu tại sao kết quả lại là như vậy nhé ❓️

2.1. Module trong JavaScript

Modules là một tính năng quan trọng trong JavaScript hiện đại, cho phép chúng ta chia nhỏ code thành các phần riêng biệt, dễ quản lý và tái sử dụng. Tuy nhiên, cách modules hoạt động có một số đặc điểm quan trọng mà chúng ta cần lưu ý.

2.2. Import và Read-only

Khi chúng ta import một giá trị từ một module khác, giá trị đó được coi là read-only (chỉ đọc). Điều này có nghĩa là chúng ta không thể trực tiếp thay đổi giá trị đã import.

Trong ví dụ của chúng ta:

// counter.js
let counter = 10;
export default counter;
// index.js
import myCounter from "./counter";

myCounter += 1; // Lỗi!

console.log(myCounter);

Khi chúng ta cố gắng thay đổi giá trị của myCounter, JavaScript sẽ throw một Error. Cụ thể, nó sẽ là một TypeError với thông báo như "Assignment to constant variable" hoặc tương tự.

2.3. Tại sao lại như vậy?

Đây là một tính năng bảo vệ trong hệ thống module của JavaScript. Nó ngăn chặn các module khác vô tình hoặc cố ý thay đổi trạng thái của module được import, giúp đảm bảo tính nhất quán và dễ dự đoán của code.

2.4. Làm thế nào để thay đổi giá trị?

Nếu bạn muốn thay đổi giá trị của một biến được export, bạn nên export một function để thay đổi giá trị đó. Ví dụ:

// counter.js
let counter = 10;

export function incrementCounter() {
  counter += 1;
  return counter;
}

export default counter;
// index.js
import myCounter, { incrementCounter } from "./counter";

console.log(myCounter); // 10
console.log(incrementCounter()); // 11
console.log(myCounter); // Vẫn là 10!

Trong ví dụ này, myCounter vẫn giữ nguyên giá trị ban đầu, nhưng chúng ta có thể thay đổi giá trị của counter trong module gốc thông qua function incrementCounter.

2.5. Tóm lại

Hiểu về tính chất read-only của các giá trị được import là rất quan trọng khi làm việc với modules trong JavaScript. Nó giúp chúng ta tránh được nhiều lỗi tiềm ẩn và viết code an toàn, dễ bảo trì hơn. Khi cần thay đổi giá trị từ một module khác, hãy nhớ sử dụng các function được export thay vì cố gắng thay đổi trực tiếp giá trị đã import.

3. Phép toán delete trong JavaScript

Output của đoạn code bên dưới là gì:

const name = "Lydia";
age = 21;

console.log(delete name);
console.log(delete age);
  • A: false, true
  • B: "Lydia", 21
  • C: true, true
  • D: undefined, undefined
Đáp án của câu hỏi này là 
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
Đáp án: A

Cùng mình đi tìm hiểu tại sao kết quả lại là như vậy nhé ❓️

3.1. Phép toán delete trong JavaScript

Phép toán delete trong JavaScript được sử dụng để xóa một thuộc tính khỏi một đối tượng. Điều quan trọng cần nhớ là:

  1. delete trả về true nếu xóa thành công hoặc nếu thuộc tính không tồn tại.
  2. delete trả về false nếu không thể xóa thuộc tính (ví dụ: thuộc tính không thể xóa được).
  3. delete không ảnh hưởng đến các biến được khai báo bằng var, let, hoặc const.

3.2. Phân tích đoạn code

Trong đoạn code của chúng ta:

const name = "Lydia";
age = 21;

console.log(delete name);
console.log(delete age);

3.2.1. Trường hợp của name

name được khai báo bằng const, do đó nó không thể bị xóa bởi phép toán delete. Vì vậy, delete name sẽ trả về false.

3.2.2. Trường hợp của age

age được khai báo mà không sử dụng var, let, hoặc const. Trong trường hợp này, JavaScript sẽ tự động tạo một thuộc tính trên đối tượng toàn cục (global object). Trong môi trường trình duyệt, đối tượng toàn cục là window.

age là một thuộc tính của đối tượng toàn cục, nó có thể bị xóa bởi phép toán delete. Do đó, delete age sẽ trả về true.

3.3. Ví dụ minh họa

Để hiểu rõ hơn, hãy xem xét ví dụ sau:

const obj = {x: 1, y: 2};
let z = 3;

console.log(delete obj.x);  // true
console.log(delete obj.y);  // true
console.log(delete z);      // false

console.log(obj);  // {}, z vẫn là 3

Trong ví dụ này:

  • delete obj.xdelete obj.y trả về true vì chúng xóa thành công các thuộc tính của đối tượng.
  • delete z trả về falsez là một biến được khai báo bằng let, không thể bị xóa bởi delete.

3.4. Tóm lại

Phép toán delete trong JavaScript chỉ có tác dụng với các thuộc tính của đối tượng. Nó không thể xóa các biến được khai báo bằng var, let, hoặc const. Khi sử dụng delete, hãy nhớ rằng nó trả về giá trị boolean chỉ ra liệu việc xóa có thành công hay không, chứ không phải giá trị của thuộc tính bị xóa.

4. Destructuring trong JavaScript

Output của đoạn code bên dưới là gì:

const numbers = [1, 2, 3, 4, 5];
const [y] = numbers;

console.log(y);
  • A: [[1, 2, 3, 4, 5]]
  • B: [1, 2, 3, 4, 5]
  • C: 1
  • D: [1]
Đáp án của câu hỏi này là 
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
Đáp án: C

Cùng mình đi tìm hiểu tại sao kết quả lại là như vậy nhé ❓️

4.1. Destructuring trong JavaScript

Destructuring là một cú pháp trong JavaScript cho phép chúng ta "giải nén" các giá trị từ mảng hoặc thuộc tính từ đối tượng vào các biến riêng biệt. Đây là một tính năng mạnh mẽ giúp code trở nên ngắn gọn và dễ đọc hơn.

4.2. Array Destructuring

Trong trường hợp của chúng ta, chúng ta đang sử dụng array destructuring:

const numbers = [1, 2, 3, 4, 5];
const [y] = numbers;

Cú pháp [y] ở đây có nghĩa là:

  • Lấy phần tử đầu tiên của mảng numbers và gán nó cho biến y.

4.3. Phân tích kết quả

y được gán giá trị của phần tử đầu tiên trong mảng numbers, nên y sẽ có giá trị là 1.

Khi chúng ta console.log(y), kết quả sẽ là 1.

4.4. Ví dụ mở rộng

Để hiểu rõ hơn về array destructuring, hãy xem xét một vài ví dụ khác:

const colors = ['red', 'green', 'blue'];

// Lấy phần tử đầu tiên
const [firstColor] = colors;
console.log(firstColor);  // 'red'

// Lấy phần tử thứ hai và thứ ba
const [, secondColor, thirdColor] = colors;
console.log(secondColor, thirdColor);  // 'green' 'blue'

// Sử dụng rest operator
const [primary, ...secondaryColors] = colors;
console.log(primary);  // 'red'
console.log(secondaryColors);  // ['green', 'blue']

4.5. Tóm lại

Array destructuring là một cách tiện lợi để trích xuất giá trị từ mảng trong JavaScript. Nó cho phép chúng ta gán các giá trị của mảng vào các biến một cách nhanh chóng và dễ đọc. Trong ví dụ của chúng ta, [y] lấy phần tử đầu tiên của mảng numbers, do đó y có giá trị là 1.

5. Spread Operator trong JavaScript

Output của đoạn code bên dưới là gì:

const user = { name: "Lydia", age: 21 };
const admin = { admin: true, ...user };

console.log(admin);
  • A: { admin: true, user: { name: "Lydia", age: 21 } }
  • B: { admin: true, name: "Lydia", age: 21 }
  • C: { admin: true, user: ["Lydia", 21] }
  • D: { admin: true }
Đáp án của câu hỏi này là 
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
↓↓↓↓↓↓↓↓↓↓
Đáp án: B

Cùng mình đi tìm hiểu tại sao kết quả lại là như vậy nhé ❓️

5.1. Spread Operator trong JavaScript

Spread Operator (...) là một tính năng mạnh mẽ trong JavaScript, được giới thiệu từ ES6. Nó cho phép chúng ta "trải" (spread) các phần tử của một đối tượng có thể lặp lại (như mảng hoặc đối tượng) thành các phần tử riêng lẻ.

5.2. Sử dụng Spread Operator với Objects

Khi sử dụng Spread Operator với objects, nó sẽ sao chép tất cả các cặp key-value của object đó vào object mới.

Trong trường hợp của chúng ta:

const user = { name: "Lydia", age: 21 };
const admin = { admin: true, ...user };

Spread Operator ...user sẽ "trải" tất cả các thuộc tính của user vào object admin.

5.3. Phân tích kết quả

Kết quả của console.log(admin) sẽ là:

{ admin: true, name: "Lydia", age: 21 }

Điều này xảy ra vì:

  1. admin: true được thêm vào object admin.
  2. ...user "trải" các thuộc tính của user (nameage) vào object admin.

5.4. Ví dụ mở rộng

Để hiểu rõ hơn về Spread Operator với objects, hãy xem xét một vài ví dụ khác:

// Kết hợp nhiều objects
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const combined = { ...obj1, ...obj2 };
console.log(combined);  // { a: 1, b: 2, c: 3, d: 4 }

// Ghi đè thuộc tính
const defaultSettings = { theme: 'light', fontSize: 12 };
const userSettings = { ...defaultSettings, theme: 'dark' };
console.log(userSettings);  // { theme: 'dark', fontSize: 12 }

// Thêm thuộc tính mới
const base = { x: 10, y: 20 };
const extended = { ...base, z: 30 };
console.log(extended);  // { x: 10, y: 20, z: 30 }

5.5. Tóm lại

Spread Operator là một công cụ mạnh mẽ trong JavaScript để làm việc với objects và arrays. Khi sử dụng với objects, nó cho phép chúng ta dễ dàng sao chép và kết hợp các thuộc tính từ nhiều objects khác nhau. Trong ví dụ của chúng ta, nó giúp tạo ra một object mới admin bao gồm cả thuộc tính admin: true và tất cả các thuộc tính của object user.

Nếu có bất kỳ câu hỏi nào đừng ngại hãy bình luận dưới phần comment nhé. Hoặc chỉ cần để lại một comment chào mình là đã giúp mình có thêm động lực hoàn thành series này. Cảm ơn các bạn rất nhiều. 🤗


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.