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

JavaScript Nâng Cao - Kỳ 18

0 0 7

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. Truthy và Falsy trong JavaScript

Bằng cách nào chúng ta có thể set hasName bằng true, nếu chúng ta không đưa true vào đối số?

function getName(name) { const hasName = //
}
  • A: !!name
  • B: name
  • C: new Boolean(name)
  • D: name.length
Đá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é ❓️

1.1. Truthy và Falsy là gì?

Trước khi đi vào giải thích đáp án, chúng ta cần hiểu về khái niệm "truthy" và "falsy" trong JavaScript.

  • Truthy: Là những giá trị mà khi ép kiểu về boolean sẽ cho kết quả true.
  • Falsy: Là những giá trị mà khi ép kiểu về boolean sẽ cho kết quả false.

Trong JavaScript, có 6 giá trị falsy:

  1. false
  2. 0 (số không)
  3. '' hoặc "" (chuỗi rỗng)
  4. null
  5. undefined
  6. NaN

Tất cả các giá trị khác đều là truthy.

1.2. Toán tử !! (Double NOT)

Toán tử !! (Double NOT) là một cách ngắn gọn để chuyển đổi một giá trị sang kiểu boolean. Nó hoạt động như sau:

  1. Toán tử ! đầu tiên chuyển đổi giá trị thành boolean và đảo ngược nó.
  2. Toán tử ! thứ hai lại đảo ngược kết quả một lần nữa.

Ví dụ:

console.log(!!0); // false
console.log(!!"hello"); // true
console.log(!!null); // false
console.log(!!1); // true

1.3. Giải thích đáp án

Quay lại câu hỏi ban đầu, chúng ta cần set hasName thành true nếu name có giá trị (không phải falsy).

  • Đáp án A (!!name): Đây là cách chính xác. !!name sẽ chuyển đổi name thành boolean. Nếu name có giá trị (truthy), !!name sẽ trả về true.

  • Đáp án B (name): Không chính xác vì nó chỉ gán giá trị của name cho hasName, không chuyển đổi thành boolean.

  • Đáp án C (new Boolean(name)): Không chính xác vì nó tạo ra một object Boolean, không phải giá trị boolean nguyên thủy.

  • Đáp án D (name.length): Không chính xác vì nó trả về độ dài của chuỗi name, không phải giá trị boolean.

1.4. Ví dụ minh họa

Hãy xem xét ví dụ sau:

function getName(name) { const hasName = !!name; console.log(hasName);
} getName("John"); // true
getName(""); // false
getName(null); // false
getName(undefined); // false

Trong ví dụ này, !!name sẽ trả về true cho bất kỳ giá trị truthy nào của name, và false cho các giá trị falsy.

1.5. Tóm lại

Sử dụng !! là một cách ngắn gọn và hiệu quả để chuyển đổi một giá trị sang kiểu boolean trong JavaScript. Nó đặc biệt hữu ích khi bạn cần kiểm tra xem một biến có giá trị hay không mà không quan tâm đến giá trị cụ thể của nó.

2. Truy cập ký tự trong chuỗi

Output của đoạn code sau là gì?

console.log("I want pizza"[0])
  • A: """
  • B: "I"
  • C: SyntaxError
  • D: undefined
Đá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. Chuỗi trong JavaScript

Trong JavaScript, chuỗi (string) là một dãy các ký tự và được coi như một mảng các ký tự. Điều này có nghĩa là chúng ta có thể truy cập vào từng ký tự của chuỗi bằng cách sử dụng chỉ số (index), tương tự như cách chúng ta truy cập các phần tử trong mảng.

2.2. Truy cập ký tự trong chuỗi

Có hai cách chính để truy cập ký tự trong chuỗi:

  1. Sử dụng dấu ngoặc vuông [] và chỉ số.
  2. Sử dụng phương thức charAt().

Trong ví dụ của chúng ta, cách thứ nhất được sử dụng.

2.3. Giải thích đáp án

Trong đoạn code "I want pizza":

  • Chuỗi "I want pizza" được coi như một mảng các ký tự.
  • `` truy cập vào phần tử đầu tiên của "mảng" này.
  • Phần tử đầu tiên (index 0) của chuỗi là ký tự "I".

Do đó, kết quả sẽ là "I", và đáp án đúng là B.

2.4. Các trường hợp khác

  • Đáp án A ("""): Không đúng vì đây không phải là cách trả về một ký tự đơn.
  • Đáp án C (SyntaxError): Không đúng vì cú pháp này hoàn toàn hợp lệ trong JavaScript.
  • Đáp án D (undefined): Không đúng vì chỉ số 0 là hợp lệ và sẽ trả về ký tự đầu tiên, không phải undefined.

2.5. Ví dụ mở rộng

Hãy xem xét một vài ví dụ khác:

console.log("Hello"[1]); // "e"
console.log("World"[4]); // "d"
console.log("JavaScript"[10]); // "t"
console.log("Python"[6]); // undefined (vì chuỗi chỉ có 6 ký tự, index từ 0-5)

2.6. Lưu ý về tương thích

Như đã đề cập trong gợi ý, phương pháp sử dụng dấu ngoặc vuông [] không hoạt động trên Internet Explorer 7 trở xuống. Trong trường hợp cần hỗ trợ các trình duyệt cũ, bạn nên sử dụng phương thức charAt():

console.log("I want pizza".charAt(0)); // "I"

2.7. Tóm lại

Truy cập ký tự trong chuỗi bằng chỉ số là một tính năng mạnh mẽ của JavaScript, cho phép chúng ta dễ dàng thao tác với từng ký tự trong chuỗi. Tuy nhiên, cần lưu ý về vấn đề tương thích với các trình duyệt cũ và sử dụng phương thức thay thế khi cần thiết.

3. Giá trị mặc định cho tham số hàm

Output của đoạn code sau là gì?

function sum(num1, num2 = num1) { console.log(num1 + num2)
} sum(10)
  • A: NaN
  • B: 20
  • C: ReferenceError
  • D: undefined
Đá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é ❓️

3.1. Giá trị mặc định cho tham số trong ES6

Từ ES6 (ECMAScript 2015) trở đi, JavaScript cho phép chúng ta định nghĩa giá trị mặc định cho các tham số của hàm. Điều này rất hữu ích khi chúng ta muốn đảm bảo rằng một tham số luôn có giá trị, ngay cả khi nó không được truyền vào khi gọi hàm.

Cú pháp cơ bản như sau:

function functionName(param1 = defaultValue1, param2 = defaultValue2) { // function body
}

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

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

function sum(num1, num2 = num1) { console.log(num1 + num2)
} sum(10)
  • num1 không có giá trị mặc định.
  • num2 có giá trị mặc định là num1.

Khi chúng ta gọi sum(10):

  1. num1 nhận giá trị 10.
  2. num2 không được truyền vào, nên nó sẽ lấy giá trị mặc định, chính là giá trị của num1 (10).
  3. Hàm thực hiện phép cộng: 10 + 10 = 20.

Do đó, kết quả in ra là 20.

3.3. Các trường hợp khác

  • Nếu chúng ta gọi sum(10, 5), kết quả sẽ là 15 vì num2 được truyền giá trị 5, không sử dụng giá trị mặc định.
  • Nếu chúng ta gọi sum(), sẽ xảy ra lỗi vì num1 không có giá trị mặc định và không được truyền giá trị.

3.4. Lưu ý quan trọng

Một điểm quan trọng cần nhớ là thứ tự của các tham số. Trong JavaScript, chúng ta chỉ có thể tham chiếu đến các tham số đã được khai báo trước đó khi đặt giá trị mặc định. Ví dụ:

// Hợp lệ
function foo(a, b = a) { ... } // Không hợp lệ, sẽ gây ra lỗi
function bar(a = b, b) { ... }

3.5. Ví dụ mở rộng

Hãy xem xét một vài ví dụ phức tạp hơn:

function greet(name, greeting = "Hello", punctuation = "!") { console.log(`${greeting}, ${name}${punctuation}`);
} greet("John"); // "Hello, John!"
greet("Mary", "Hi"); // "Hi, Mary!"
greet("Tom", "Welcome", "..."); // "Welcome, Tom..."

3.6. Tóm lại

Giá trị mặc định cho tham số là một tính năng mạnh mẽ trong JavaScript hiện đại. Nó giúp code của chúng ta ngắn gọn hơn, dễ đọc hơn và giảm thiểu các lỗi có thể xảy ra khi tham số không được truyền vào. Tuy nhiên, cần lưu ý về thứ tự khai báo và sử dụng các tham số để tránh các lỗi không mong muốn.

4. Export và Import trong ES6 Modules

Output của đoạn code sau là gì?

// module.js 
export default () => "Hello world"
export const name = "Lydia" // index.js 
import * as data from "./module" console.log(data)
  • A: { default: function default(), name: "Lydia" }
  • B: { default: function default() }
  • C: { default: "Hello world", name: "Lydia" }
  • D: Global object of module.js
Đá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é ❓️

4.1. ES6 Modules

ES6 (ECMAScript 2015) giới thiệu hệ thống module mới cho JavaScript, cho phép chúng ta chia code thành các phần nhỏ, độc lập và có thể tái sử dụng. Hai khái niệm quan trọng trong ES6 modules là exportimport.

4.2. Export trong ES6

Có hai loại export chính:

  1. Named exports: Cho phép export nhiều giá trị từ một module.
  2. Default export: Cho phép export một giá trị mặc định từ module.

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

// module.js 
export default () => "Hello world"
export const name = "Lydia"
  • export default () => "Hello world" là một default export.
  • export const name = "Lydia" là một named export.

4.3. Import trong ES6

Có nhiều cách để import trong ES6:

  1. Import named exports
  2. Import default export
  3. Import tất cả như một object

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

import * as data from "./module"

Đây là cách import tất cả các export (cả default và named) từ module như một object.

4.4. Giải thích đáp án

Khi chúng ta sử dụng import * as data, JavaScript tạo ra một object chứa tất cả các export từ module:

  • Default export được gán cho thuộc tính default của object.
  • Named exports được gán cho các thuộc tính có tên tương ứng.

Do đó, data sẽ có dạng:

{ default: function default() { return "Hello world" }, name: "Lydia"
}

Đây chính xác là đáp án A.

4.5. Các đáp án khác

  • Đáp án B không đúng vì nó bỏ qua named export name.
  • Đáp án C không đúng vì nó hiển thị giá trị trả về của hàm default thay vì chính hàm đó.
  • Đáp án D không đúng vì import * as data không tạo ra global object.

4.6. Ví dụ mở rộng

Hãy xem xét một vài cách import khác:

// Chỉ import default export
import myFunction from "./module" // Import named export
import { name } from "./module" // Import cả default và named exports
import myFunction, { name } from "./module" // Đổi tên khi import
import { name as myName } from "./module"

4.7. Lưu ý quan trọng

  • Default export chỉ có thể có một trong mỗi module.
  • Tên của default import có thể tùy chọn, không nhất thiết phải trùng với tên trong module gốc.
  • Named exports phải được import với đúng tên (trừ khi sử dụng alias).

4.8. Tóm lại

ES6 Modules cung cấp một cách mạnh mẽ để tổ chức và chia sẻ code trong JavaScript. Hiểu rõ cách hoạt động của exportimport sẽ giúp bạn xây dựng các ứng dụng JavaScript lớn và có cấu trúc tốt hơn. Việc sử dụng import * as như trong ví dụ này cho phép bạn import tất cả các export từ một module dưới dạng một object, giúp quản lý các import dễ dàng hơn trong các module lớn.

5. Classes trong JavaScript

Output của đoạn code sau là gì?

class Person { constructor(name) { this.name = name }
} const member = new Person("John")
console.log(typeof member)
  • A: "class"
  • B: "function"
  • C: "object"
  • D: "string"
Đá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é ❓️

5.1. Classes trong JavaScript

Classes được giới thiệu trong ES6 như một cú pháp mới để định nghĩa các đối tượng và kế thừa dựa trên prototype. Tuy nhiên, điều quan trọng cần nhớ là classes trong JavaScript chỉ là "syntactic sugar" (cú pháp đường) cho cơ chế prototype-based inheritance đã tồn tại từ trước.

5.2. Constructor trong Classes

Constructor là một phương thức đặc biệt được sử dụng để khởi tạo các đối tượng mới được tạo ra từ class. Nó được gọi tự động khi chúng ta sử dụng từ khóa new để tạo một instance mới của class.

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

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

class Person { constructor(name) { this.name = name }
} const member = new Person("John")
console.log(typeof member)
  1. Chúng ta định nghĩa một class Person với một constructor nhận vào tham số name.
  2. Constructor này gán giá trị của name cho thuộc tính this.name của instance.
  3. Chúng ta tạo một instance mới của Person với new Person("John") và gán nó cho biến member.
  4. Cuối cùng, chúng ta kiểm tra kiểu của member bằng typeof.

5.4. Giải thích đáp án

Kết quả của typeof member"object". Đây là bởi vì:

  1. Khi chúng ta sử dụng new với một class, nó tạo ra một đối tượng mới.
  2. Trong JavaScript, classes thực chất là các hàm đặc biệt, nhưng instances của chúng là các đối tượng.
  3. typeof trả về "object" cho bất kỳ đối tượng nào không phải là một hàm.

5.5. Các đáp án khác

  • Đáp án A ("class") không đúng vì class không phải là một kiểu dữ liệu trong JavaScript.
  • Đáp án B ("function") không đúng vì mặc dù class là một loại function đặc biệt, nhưng instance của nó là một object.
  • Đáp án D ("string") không đúng vì member không phải là một chuỗi.

5.6. Classes vs Function Constructors

Để hiểu rõ hơn về cách classes hoạt động, hãy so sánh nó với cách viết tương đương sử dụng function constructor:

// Sử dụng Class
class Person { constructor(name) { this.name = name; }
} // Tương đương với Function Constructor
function Person(name) { this.name = name;
} // Cả hai cách đều cho kết quả giống nhau
const member1 = new Person("John");
const member2 = new Person("John");
console.log(typeof member1); // "object"
console.log(typeof member2); // "object"

5.7. Lưu ý quan trọng

  • Classes trong JavaScript không tạo ra một kiểu dữ liệu mới. Chúng chỉ là một cách khác để định nghĩa và làm việc với objects.
  • instanceof operator có thể được sử dụng để kiểm tra xem một object có phải là instance của một class cụ thể hay không.

5.8. Tóm lại

Classes trong JavaScript cung cấp một cú pháp rõ ràng và dễ hiểu hơn để làm việc với objects và kế thừa. Tuy nhiên, điều quan trọng là phải hiểu rằng chúng vẫn dựa trên cơ chế prototype-based của JavaScript. Khi chúng ta tạo một instance của class bằng từ khóa new, kết quả là một object thông thường, và đó là lý do tại sao typeof trả về "object".

Kết luận

Qua 5 ví dụ trên, chúng ta đã đi sâu vào một số khía cạnh thú vị của JavaScript:

  1. Cách sử dụng toán tử !! để chuyển đổi giá trị sang boolean.
  2. Truy cập ký tự trong chuỗi bằng index.
  3. Sử dụng giá trị mặc định cho tham số hàm.
  4. Cách hoạt động của export và import trong ES6 modules.
  5. Bản chất của classes và instances trong JavaScript.

Những kiến thức này không chỉ giúp bạn hiểu rõ hơn về cách JavaScript hoạt động, mà còn có thể áp dụng vào thực tế để viết code hiệu quả và tránh các lỗi phổ biến.

Hãy nhớ rằng, JavaScript là một ngôn ngữ rất linh hoạt và có nhiều cách để thực hiện cùng một tác vụ. Việc hiểu rõ các khái niệm cơ bản và các tính năng trong ES6 sẽ giúp bạn chọn cách tiếp cận tốt nhất cho từng tình huống cụ thể.

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