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

JavaScript Nâng Cao - Kỳ 20

0 0 5

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

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. Gán lại giá trị cho class

class Person { constructor() { this.name = "Lydia" }
} Person = class AnotherPerson { constructor() { this.name = "Sarah" }
} const member = new Person()
console.log(member.name)
  • A: "Lydia"
  • B: "Sarah"
  • C: Error: cannot redeclare Person
  • D: SyntaxError
Đá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. Class trong JavaScript

Trước hết, chúng ta cần hiểu rằng trong JavaScript, class thực chất chỉ là "syntactic sugar" cho constructor function. Khi chúng ta khai báo một class, JavaScript engine sẽ tạo ra một function constructor tương ứng.

1.2. Gán lại giá trị cho class

Trong JavaScript, chúng ta có thể gán lại giá trị cho một biến, kể cả khi biến đó đang giữ một class. Điều này có nghĩa là chúng ta có thể "thay đổi" định nghĩa của một class sau khi nó đã được khai báo.

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

Trong đoạn code trên, chúng ta làm hai việc:

  1. Khai báo class Person với constructor set this.name = "Lydia".
  2. Gán lại Person bằng một class mới AnotherPerson với constructor set this.name = "Sarah".

Khi chúng ta tạo một instance mới của Person bằng new Person(), JavaScript sẽ sử dụng định nghĩa mới nhất của Person, tức là AnotherPerson.

1.4. Ví dụ minh họa

Để hiểu rõ hơn, hãy tưởng tượng bạn đang xây dựng một ngôi nhà. Ban đầu, bạn có bản thiết kế A (class Person ban đầu). Nhưng sau đó, bạn quyết định thay đổi hoàn toàn thiết kế và sử dụng bản thiết kế B (class AnotherPerson). Khi bạn bắt đầu xây nhà (tạo instance), bạn sẽ sử dụng bản thiết kế mới nhất, tức là B.

1.5. Lưu ý quan trọng

Điều quan trọng cần nhớ là việc gán lại giá trị cho class như thế này có thể gây ra những hậu quả không mong muốn trong code của bạn. Nó có thể làm cho code trở nên khó đọc và khó bảo trì. Trong thực tế, bạn nên tránh việc gán lại giá trị cho class như vậy để đảm bảo tính nhất quán của code.

2. Symbol và Object.keys()

const info = { [Symbol('a')]: 'b'
} console.log(info)
console.log(Object.keys(info))
  • A: {Symbol('a'): 'b'}["{Symbol('a')"]
  • B: {}[]
  • C: { a: "b" }["a"]
  • D: {Symbol('a'): 'b'}[]
Đá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é ❓️

2.1. Symbol trong JavaScript

Symbol là một kiểu dữ liệu nguyên thủy mới được giới thiệu trong ECMAScript 2015 (ES6). Mỗi Symbol là duy nhất và không thể thay đổi (immutable).

2.2. Object với Symbol key

Khi chúng ta sử dụng Symbol làm key cho một object, nó sẽ không hiển thị như một key thông thường. Đây là một trong những đặc điểm quan trọng của Symbol - nó có thể được sử dụng để tạo ra các thuộc tính "ẩn" trong object.

2.3. Object.keys() và Symbol

Object.keys() là một phương thức trả về một mảng chứa tất cả các key có thể đếm được (enumerable) của một object. Tuy nhiên, Symbol keys không được coi là enumerable theo mặc định.

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

Trong đoạn code trên:

  1. console.log(info) sẽ hiển thị toàn bộ object, bao gồm cả Symbol key. Kết quả là {Symbol('a'): 'b'}.

  2. console.log(Object.keys(info)) sẽ trả về một mảng rỗng [] vì không có key nào là enumerable trong object này.

2.5. Ví dụ minh họa

Hãy tưởng tượng bạn có một chiếc hộp (object) với một chiếc chìa khóa đặc biệt (Symbol). Khi bạn nhìn vào hộp, bạn có thể thấy chiếc chìa khóa đó. Nhưng khi bạn cố gắng liệt kê tất cả các chìa khóa thông thường, chiếc chìa khóa đặc biệt này sẽ không xuất hiện trong danh sách.

2.6. Lưu ý quan trọng

Mặc dù Symbol keys không xuất hiện trong Object.keys(), chúng vẫn có thể được truy cập bằng các phương thức khác như Object.getOwnPropertySymbols() hoặc Reflect.ownKeys().

Ví dụ:

console.log(Object.getOwnPropertySymbols(info)); // [Symbol(a)]
console.log(Reflect.ownKeys(info)); // [Symbol(a)]

Symbol là một công cụ mạnh mẽ để tạo ra các thuộc tính "riêng tư" trong JavaScript, nhưng cần sử dụng cẩn thận để tránh gây nhầm lẫn cho các lập trình viên khác khi đọc code của bạn.

3. Destructuring và Arrow Function

const getList = ([x, ...y]) => [x, y]
const getUser = user => { name: user.name, age: user.age } const list = [1, 2, 3, 4]
const user = { name: "Lydia", age: 21 } console.log(getList(list))
console.log(getUser(user))
  • A: [1, [2, 3, 4]]undefined
  • B: [1, [2, 3, 4]]{ name: "Lydia", age: 21 }
  • C: [1, 2, 3, 4]{ name: "Lydia", age: 21 }
  • D: Error{ name: "Lydia", age: 21 }
Đá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. Destructuring trong JavaScript

Destructuring là một cú pháp cho phép chúng ta "giải nén" các giá trị từ mảng hoặc thuộc tính từ objects vào các biến riêng biệt.

3.2. Rest parameter

Rest parameter là một cú pháp cho phép chúng ta đại diện cho một số lượng không xác định các đối số dưới dạng một mảng.

3.3. Arrow function

Arrow function là một cú pháp ngắn gọn để viết function expression. Tuy nhiên, nó có một số khác biệt so với function thông thường, đặc biệt là cách xử lý returnthis.

3.4. Phân tích hàm getList

const getList = ([x, ...y]) => [x, y]

Hàm này sử dụng destructuring và rest parameter. Khi gọi getList(list):

  • x sẽ lấy phần tử đầu tiên của list (1)
  • ...y sẽ lấy tất cả các phần tử còn lại và đưa vào một mảng mới ([2, 3, 4])

Kết quả trả về là một mảng chứa xy: [1, [2, 3, 4]]

3.5. Phân tích hàm getUser

const getUser = user => { name: user.name, age: user.age }

Đây là nơi xảy ra lỗi. Trong arrow function, khi chúng ta muốn trả về một object ngay lập tức, chúng ta cần bọc object đó trong dấu ngoặc tròn. Nếu không, JavaScript sẽ coi { là bắt đầu của function body, không phải là một object literal.

Cách sửa:

const getUser = user => ({ name: user.name, age: user.age })

Vì không có dấu ngoặc tròn, JavaScript coi đây là function body, nhưng không có return statement, nên hàm trả về undefined.

3.6. Ví dụ minh họa

Hãy tưởng tượng bạn có một hộp đồ chơi (mảng) và bạn muốn lấy ra đồ chơi đầu tiên và đặt tất cả đồ chơi còn lại vào một hộp khác. Đó chính là những gì getList đang làm.

Với getUser, tưởng tượng bạn đang cố gắng đóng gói thông tin của một người vào một hộp, nhưng bạn quên đóng nắp hộp (thiếu dấu ngoặc tròn), nên khi bạn mở hộp ra, không có gì bên trong (undefined).

3.7. Lưu ý quan trọng

Khi sử dụng arrow function để trả về object literal ngay lập tức, luôn nhớ bọc object trong dấu ngoặc tròn để tránh nhầm lẫn với function body.

4. Gọi một biến như một hàm

const name = "Lydia" console.log(name())
  • A: SyntaxError
  • B: ReferenceError
  • C: TypeError
  • D: undefined
Đá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. Kiểu dữ liệu trong JavaScript

JavaScript có các kiểu dữ liệu nguyên thủy (primitive types) như string, number, boolean, null, undefined, symbol, và một kiểu dữ liệu phức tạp là object (bao gồm cả function, vì trong JavaScript, function cũng là object).

4.2. Gọi hàm trong JavaScript

Trong JavaScript, chúng ta có thể gọi một hàm bằng cách thêm dấu ngoặc đơn () sau tên hàm. Tuy nhiên, điều này chỉ hoạt động khi biến đó thực sự chứa một hàm.

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

Trong đoạn code trên:

  1. Chúng ta khai báo một biến name và gán cho nó giá trị là chuỗi "Lydia".
  2. Sau đó, chúng ta cố gắng gọi name như một hàm bằng cách sử dụng name().

4.4. TypeError

Khi chúng ta cố gắng sử dụng một giá trị không phải là hàm như một hàm, JavaScript sẽ throw ra một TypeError. Cụ thể, lỗi sẽ là TypeError: name is not a function.

4.5. Các loại lỗi khác trong JavaScript

  1. SyntaxError: Xảy ra khi có lỗi cú pháp trong code.
  2. ReferenceError: Xảy ra khi chúng ta cố gắng sử dụng một biến chưa được khai báo.
  3. TypeError: Xảy ra khi một giá trị không phải là kiểu dữ liệu mong đợi, như trong trường hợp này.

4.7. Lưu ý quan trọng

Luôn đảm bảo rằng bạn đang gọi một hàm, không phải một kiểu dữ liệu khác. Trong JavaScript, bạn có thể kiểm tra xem một biến có phải là hàm hay không bằng cách sử dụng toán tử typeof:

const name = "Lydia";
console.log(typeof name); // "string"
console.log(typeof name === "function"); // false

5. Template Literals và Short-circuit Evaluation

const output = `${[] && 'Im'}possible!
You should${'' && `n't`} see a therapist after so much JavaScript lol`
  • A: possible! You should see a therapist after so much JavaScript lol
  • B: Impossible! You should see a therapist after so much JavaScript lol
  • C: possible! You shouldn't see a therapist after so much JavaScript lol
  • D: Impossible! You shouldn't see a therapist after so much JavaScript lol
Đá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. Template Literals

Template literals là một cách để tạo chuỗi trong JavaScript, cho phép chúng ta nhúng biểu thức vào trong chuỗi bằng cách sử dụng cú pháp ${}.

5.2. Short-circuit Evaluation

Short-circuit evaluation là một kỹ thuật trong JavaScript (và nhiều ngôn ngữ khác) nơi toán tử logic (&&||) được sử dụng để đánh giá biểu thức từ trái sang phải, dừng lại ngay khi kết quả được xác định.

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

Hãy chia nhỏ biểu thức template literal:

  1. ${[] && 'Im'}:

    • [] là một mảng rỗng, được coi là truthy trong JavaScript.
    • Do đó, [] && 'Im' sẽ trả về 'Im'.
  2. ${'' &&n't}:

    • '' là một chuỗi rỗng, được coi là falsy trong JavaScript.
    • Do đó, '' &&n't`` sẽ trả về '' (chuỗi rỗng).

5.4. Kết quả cuối cùng

Khi ghép tất cả lại, chúng ta có:

Impossible!
You should see a therapist after so much JavaScript lol

5.5. Lưu ý quan trọng

Short-circuit evaluation là một công cụ mạnh mẽ trong JavaScript, nhưng nó cũng có thể làm cho code trở nên khó đọc nếu sử dụng quá nhiều. Trong thực tế, bạn nên cân nhắc giữa việc sử dụng short-circuit evaluation và viết code rõ ràng, dễ đọc.

Kết luận

Qua 5 ví dụ trên, chúng ta đã đi sâu vào một số câu hỏi thú vị trong JavaScript:

  1. Cách class hoạt động và có thể được gán lại.
  2. Symbol và cách nó ảnh hưởng đến các phương thức như Object.keys().
  3. Destructuring, rest parameters và cách viết arrow function trả về object.
  4. Sự khác biệt giữa các loại lỗi trong JavaScript.
  5. Template literals và short-circuit evaluation.

Hy vọng qua bài viết này, các bạn đã có thêm những kiến thức bổ ích về JavaScript.

Hẹn gặp lại các bạn trong những bài viết tiếp theo của series "JavaScript Nâng Cao" nhé!

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