- vừa được xem lúc

JavaScript Nâng Cao - Kỳ 14

0 0 9

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

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. Kế thừa trong JavaScript

Với cách nào chúng ta có thể kế thừa Dog class?

class Dog { constructor(name) { this.name = name; }
}; class Labrador extends Dog { // 1  constructor(name, size) { this.size = size; } // 2 constructor(name, size) { super(name); this.size = size; } // 3 constructor(size) { super(name); this.size = size; } // 4  constructor(name, size) { this.name = name; this.size = size; } };
  • A: 1
  • B: 2
  • C: 3
  • D: 4
Đá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é ❓️

1.1. Kế thừa trong JavaScript

Trong JavaScript, kế thừa là một cơ chế cho phép một class (lớp con) kế thừa các thuộc tính và phương thức từ một class khác (lớp cha). Điều này giúp tái sử dụng code và tạo ra mối quan hệ phân cấp giữa các class.

1.2. Từ khóa extendssuper

  • extends: Được sử dụng để tạo một class là class con của một class khác.
  • super: Được sử dụng để gọi constructor của lớp cha.

1.3. Phân tích các cách kế thừa

  1. Cách 1:

    constructor(name, size) { this.size = size;
    }
    

    Lỗi: Không gọi super() trước khi sử dụng this.

  2. Cách 2:

    constructor(name, size) { super(name); this.size = size;
    }
    

    Đúng: Gọi super(name) trước, sau đó khởi tạo thuộc tính mới.

  3. Cách 3:

    constructor(size) { super(name); this.size = size;
    }
    

    Lỗi: name không được định nghĩa trong tham số của constructor.

  4. Cách 4:

    constructor(name, size) { this.name = name; this.size = size;
    }
    

    Lỗi: Không gọi super() trong constructor của lớp con.

1.4. Tại sao cách 2 là đúng?

  1. Gọi super(name): Điều này đảm bảo constructor của lớp cha (Dog) được gọi với tham số name.
  2. Khởi tạo this.size: Sau khi gọi super(), ta có thể an toàn sử dụng this để thêm thuộc tính mới.

1.5. Lưu ý quan trọng

  • Trong constructor của lớp con, ta phải gọi super() trước khi sử dụng this.
  • super() phải được gọi với đúng số lượng và kiểu tham số mà constructor của lớp cha yêu cầu.

Hiểu được cách kế thừa đúng đắn sẽ giúp bạn tránh được nhiều lỗi khi làm việc với OOP trong JavaScript. Hãy luôn nhớ gọi super() đúng cách trong constructor của lớp con!

2. Module Import và Thứ tự Thực thi

Output là gì?

// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2)); // sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
  • A: running index.js, running sum.js, 3
  • B: running sum.js, running index.js, 3
  • C: running sum.js, 3, running index.js
  • D: running index.js, undefined, running sum.js
Đá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é ❓️

2.1. Module trong JavaScript

Module là một cách để tổ chức và chia nhỏ code thành các phần độc lập, có thể tái sử dụng. ES6 đã giới thiệu cú pháp importexport để làm việc với module.

2.2. Thứ tự thực thi của Module

Khi sử dụng import, JavaScript sẽ thực hiện theo các bước sau:

  1. Parsing (Phân tích): JavaScript engine đọc và phân tích toàn bộ file.
  2. Hoisting của Import: Tất cả các câu lệnh import được đưa lên đầu scope.
  3. Module Resolution và Loading: Các module được import sẽ được tải và thực thi.
  4. Evaluation (Đánh giá): Code trong file hiện tại được thực thi.

2.3. Phân tích ví dụ

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

  1. index.js được phân tích.
  2. import { sum } from './sum.js' được hoisted.
  3. sum.js được tải và thực thi, in ra running sum.js.
  4. Quay lại index.js, in ra running index.js.
  5. sum(1, 2) được gọi và in ra kết quả 3.

2.4. So sánh với CommonJS (require)

Nếu chúng ta sử dụng require thay vì import:

// index.js
console.log('running index.js');
const { sum } = require('./sum.js');
console.log(sum(1, 2)); // sum.js
console.log('running sum.js');
exports.sum = (a, b) => a + b;

Kết quả sẽ là: running index.js, running sum.js, 3

Điều này là do require là đồng bộ và được thực thi tại thời điểm nó được gọi.

2.5. Tại sao điều này quan trọng?

Hiểu về thứ tự thực thi của module giúp chúng ta:

  • Tránh circular dependencies (phụ thuộc vòng).
  • Tối ưu hóa performance bằng cách sắp xếp imports hợp lý.
  • Debug dễ dàng hơn khi gặp vấn đề liên quan đến module.

Nhớ rằng, với ES modules, code trong module imported luôn được thực thi trước code trong module importing!

3. So sánh Primitive và Object trong JavaScript

Output là gì?

console.log(Number(2) === Number(2))
console.log(Boolean(false) === Boolean(false))
console.log(Symbol('foo') === Symbol('foo'))
  • A: true, true, false
  • B: false, true, false
  • C: true, false, true
  • D: true, true, true
Đá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. Primitive vs Object trong JavaScript

JavaScript có hai loại dữ liệu chính:

  1. Primitive: number, string, boolean, null, undefined, symbol, bigint
  2. Object: object, array, function, etc.

3.2. So sánh các giá trị Primitive

Khi so sánh các giá trị primitive, JavaScript so sánh giá trị của chúng.

console.log(Number(2) === Number(2)) // true
console.log(Boolean(false) === Boolean(false)) // true

Trong cả hai trường hợp này, chúng ta đang so sánh các giá trị primitive, nên kết quả là true.

3.3. Symbol - Trường hợp đặc biệt

console.log(Symbol('foo') === Symbol('foo')) // false

Symbol là một trường hợp đặc biệt. Mỗi Symbol là một giá trị duy nhất, ngay cả khi chúng có cùng mô tả.

3.4. Tại sao Symbol('foo') !== Symbol('foo')?

  1. Tính duy nhất: Mỗi Symbol được tạo ra là duy nhất, bất kể mô tả của nó.
  2. Mô tả chỉ để debug: Chuỗi truyền vào Symbol() chỉ là mô tả, không ảnh hưởng đến giá trị của Symbol.
  3. Use-case: Symbols thường được sử dụng làm key duy nhất trong objects.

3.5. Ví dụ minh họa về Symbol

const sym1 = Symbol('description');
const sym2 = Symbol('description'); console.log(sym1 === sym2); // false
console.log(sym1.description === sym2.description); // true

3.6. Lưu ý quan trọng

  • Khi so sánh objects (bao gồm arrays và functions), JavaScript so sánh tham chiếu, không phải giá trị.
  • Để so sánh giá trị của objects, bạn cần so sánh từng thuộc tính hoặc sử dụng các phương pháp deep comparison.

Hiểu rõ về cách JavaScript so sánh các kiểu dữ liệu khác nhau sẽ giúp bạn tránh được nhiều lỗi không mong muốn trong quá trình phát triển!

4. Phương thức padStart() trong JavaScript

Output là gì?

const name = "Lydia Hallie"
console.log(name.padStart(13))
console.log(name.padStart(2))
  • A: "Lydia Hallie", "Lydia Hallie"
  • B: " Lydia Hallie", " Lydia Hallie" ("[13x whitespace]Lydia Hallie", "[2x whitespace]Lydia Hallie")
  • C: " Lydia Hallie", "Lydia Hallie" ("[1x whitespace]Lydia Hallie", "Lydia Hallie")
  • D: "Lydia Hallie", "Lyd",
Đá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. Phương thức padStart()

padStart() là một phương thức của String trong JavaScript, được sử dụng để thêm padding (ký tự đệm) vào đầu chuỗi cho đến khi chuỗi đạt được độ dài chỉ định.

Cú pháp:

str.padStart(targetLength [, padString])
  • targetLength: Độ dài mong muốn của chuỗi kết quả.
  • padString (tùy chọn): Chuỗi để đệm. Mặc định là khoảng trắng.

4.2. Phân tích ví dụ

const name = "Lydia Hallie" // Độ dài: 12
console.log(name.padStart(13))
console.log(name.padStart(2))
  1. name.padStart(13):
    • Độ dài mong muốn (13) > Độ dài hiện tại (12)
    • Thêm 1 khoảng trắng vào đầu chuỗi
    • Kết quả: " Lydia Hallie"
  2. name.padStart(2):
    • Độ dài mong muốn (2) < Độ dài hiện tại (12)
    • Không thêm khoảng trắng nào
    • Kết quả: "Lydia Hallie" (giữ nguyên chuỗi gốc)

4.3. Các ví dụ khác về padStart()

console.log("5".padStart(3, "0")); // "005"
console.log("123".padStart(5, "*")); // "**123"
console.log("abc".padStart(10, "foo")); // "foofoofabc"

4.4. Use cases của padStart()

  1. Định dạng số: Thêm số 0 vào đầu số.

    const formattedNumber = "42".padStart(5, "0"); // "00042"
    
  2. Căn chỉnh text: Tạo khoảng trắng đều cho các chuỗi.

    const items = ["Apple", "Banana", "Orange"];
    items.forEach(item => console.log(item.padStart(10)));
    // " Apple"
    // " Banana"
    // " Orange"
    
  3. Mã hóa thông tin: Ẩn một phần thông tin.

    const lastFourDigits = "1234";
    const maskedNumber = lastFourDigits.padStart(16, "*"); // "************1234"
    

4.5. Lưu ý quan trọng

  • Nếu targetLength nhỏ hơn độ dài chuỗi hiện tại, chuỗi sẽ không bị cắt ngắn mà giữ nguyên.
  • Nếu padString quá dài, nó sẽ bị cắt ngắn để phù hợp với targetLength.

Hiểu và sử dụng padStart() hiệu quả có thể giúp bạn xử lý chuỗi một cách linh hoạt và chuyên nghiệp hơn trong JavaScript!

5. Nối chuỗi với Emoji trong JavaScript

Output là gì?

console.log("🥑" + "💻");
  • A: "🥑💻"
  • B: 257548
  • C: A string containing their code points
  • D: Error
Đá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é ❓️

5.1. Emoji trong JavaScript

Emoji là các ký tự Unicode, và trong JavaScript, chúng được xử lý như các chuỗi bình thường.

5.2. Phép toán cộng chuỗi

Trong JavaScript, khi sử dụng toán tử + với các chuỗi, nó sẽ thực hiện phép nối chuỗi.

console.log("🥑" + "💻"); // "🥑💻"

Kết quả là một chuỗi mới chứa cả hai emoji.

5.3. Code points của Emoji

Mỗi emoji có một code point Unicode riêng:

console.log("🥑".codePointAt(0)); // 129361
console.log("💻".codePointAt(0)); // 128187

Tuy nhiên, khi nối chuỗi, JavaScript không quan tâm đến code points mà chỉ đơn giản là ghép các ký tự lại với nhau.

5.4. Các ví dụ khác về xử lý Emoji

  1. Đếm độ dài chuỗi chứa Emoji:

    console.log("🥑💻".length); // 4 (không phải 2)
    

    Lưu ý: Nhiều emoji được biểu diễn bằng 2 "code units" trong JavaScript.

  2. Lặp qua từng ký tự trong chuỗi Emoji:

    for (let char of "🥑💻") { console.log(char);
    }
    // 🥑
    // 💻
    
  3. Sử dụng Emoji trong template literals:

    const fruit = "🥑";
    const device = "💻";
    console.log(`I love ${fruit} and ${device}`); // "I love 🥑 and 💻"
    

5.5. Lưu ý quan trọng

  • Emoji có thể gây ra vấn đề khi tính toán độ dài chuỗi hoặc cắt chuỗi.
  • Một số emoji phức tạp (như emoji với tông màu da) có thể được tạo thành từ nhiều Unicode code points.
  • Khi làm việc với emoji, nên sử dụng các thư viện chuyên dụng để xử lý chuỗi Unicode một cách chính xác.

Hiểu về cách JavaScript xử lý emoji sẽ giúp bạn tránh được nhiều lỗi không mong muốn khi làm việc với chuỗi trong các ứng dụng hiện đại!

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. 🤗

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 518

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 429

- vừa được xem lúc

Một số phương thức với object trong Javascript

Trong Javascript có hỗ trợ các loại dữ liệu cơ bản là giống với hầu hết những ngôn ngữ lập trình khác. Bài viết này mình sẽ giới thiệu về Object và một số phương thức thường dùng với nó.

0 0 147

- vừa được xem lúc

Tìm hiểu về thư viện axios

Giới thiệu. Axios là gì? Axios là một thư viện HTTP Client dựa trên Promise.

0 0 135

- vừa được xem lúc

Imports và Exports trong JavaScript ES6

. Giới thiệu. ES6 cung cấp cho chúng ta import (nhập), export (xuất) các functions, biến từ module này sang module khác và sử dụng nó trong các file khác.

0 0 105

- vừa được xem lúc

Bài toán đọc số thành chữ (phần 2) - Hoàn chỉnh chương trình dưới 100 dòng code

Tiếp tục bài viết còn dang dở ở phần trước Phân tích bài toán đọc số thành chữ (phần 1) - Phân tích đề và những mảnh ghép đầu tiên. Bạn nào chưa đọc thì có thể xem ở link trên trước nhé.

0 0 241