+11

JavaScript Nâng Cao - Kỳ 5

Có một câu nói 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. Hàm eval trong JavaScript

Giá trị của sum là gì?

const sum = eval("10*10+5");
  • A: 105
  • B: "105"
  • C: TypeError
  • D: "10*10+5"
Đá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. Hàm eval là gì?

Hàm eval trong JavaScript là một hàm đặc biệt cho phép chúng ta thực thi một đoạn mã JavaScript được biểu diễn dưới dạng chuỗi. Nó sẽ đánh giá và thực thi đoạn mã đó.

Ví dụ:

const result = eval("2 + 2");
console.log(result); // Output: 4

Trong ví dụ trên, hàm eval sẽ đánh giá biểu thức "2 + 2" và trả về kết quả là 4.

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

Quay lại với đoạn code ban đầu:

const sum = eval("10*10+5");

Ở đây, chúng ta truyền vào hàm eval một chuỗi chứa biểu thức toán học "10*10+5". Hàm eval sẽ đánh giá biểu thức này và tính toán kết quả.

Biểu thức "10*10+5" tương đương với phép tính 10 nhân 10 cộng 5, kết quả sẽ là 105.

Vì vậy, giá trị của biến sum sẽ là 105, đúng với đáp án A.

1.3. Lưu ý khi sử dụng eval

Mặc dù hàm eval có thể hữu ích trong một số trường hợp, nhưng nó cũng tiềm ẩn nhiều rủi ro bảo mật. Nếu chúng ta sử dụng eval với dữ liệu đầu vào không đáng tin cậy (ví dụ: dữ liệu từ người dùng), kẻ tấn công có thể chèn mã độc vào và thực thi nó.

Do đó, tốt nhất là nên tránh sử dụng eval nếu có thể. Thay vào đó, hãy cố gắng tìm các giải pháp thay thế an toàn hơn.

2. Lưu trữ dữ liệu với sessionStorage

Biến cool_secret sẽ truy cập được trong bao lâu?

sessionStorage.setItem("cool_secret", 123);
  • A: Mãi mãi, dữ liệu sẽ không bao giờ mất.
  • B: Khi user đóng tab lại.
  • C: Khi user không chỉ là đóng tab, mà đóng browser lại.
  • D: Khi user tắt máy tính đi.
Đá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. sessionStorage là gì?

sessionStorage là một đối tượng trong Web Storage API của JavaScript. Nó cho phép chúng ta lưu trữ dữ liệu dưới dạng cặp key-value trong trình duyệt.

Đặc điểm của sessionStorage là dữ liệu sẽ tồn tại trong phiên làm việc (session) của người dùng. Khi người dùng đóng tab hoặc cửa sổ trình duyệt, dữ liệu trong sessionStorage sẽ bị xóa.

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

Trong đoạn code trên, chúng ta sử dụng phương thức setItem của sessionStorage để lưu trữ một cặp key-value. Key là "cool_secret" và value là 123.

sessionStorage.setItem("cool_secret", 123);

Điều này có nghĩa là chúng ta đang lưu trữ giá trị 123 với key "cool_secret" trong sessionStorage.

2.3. Thời gian tồn tại của dữ liệu trong sessionStorage

Như đã đề cập, dữ liệu trong sessionStorage sẽ tồn tại trong phiên làm việc của người dùng. Khi người dùng đóng tab hoặc cửa sổ trình duyệt, dữ liệu sẽ bị xóa.

Vì vậy, đáp án đúng cho câu hỏi này là B: Khi user đóng tab lại.

2.4. So sánh với localStorage

Ngoài sessionStorage, JavaScript còn cung cấp một đối tượng khác để lưu trữ dữ liệu là localStorage. Sự khác biệt chính giữa sessionStoragelocalStorage là:

  • Dữ liệu trong sessionStorage sẽ bị xóa khi đóng tab hoặc cửa sổ trình duyệt.
  • Dữ liệu trong localStorage sẽ tồn tại vĩnh viễn cho đến khi bị xóa thủ công hoặc xóa bởi JavaScript.

Nếu chúng ta muốn lưu trữ dữ liệu lâu dài, không bị mất khi đóng trình duyệt, thì localStorage sẽ là lựa chọn phù hợp hơn.

3. Khai báo biến với var

Output là gì?

var num = 8;
var num = 10;

console.log(num);
  • A: 8
  • B: 10
  • C: SyntaxError
  • D: ReferenceError
Đá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. Khai báo biến với var

Trong JavaScript, từ khóa var được sử dụng để khai báo biến. Một đặc điểm của var là nó cho phép khai báo lại biến với cùng tên mà không gây ra lỗi.

Khi chúng ta khai báo lại một biến với var, giá trị của biến sẽ được cập nhật thành giá trị mới nhất.

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

Hãy xem xét đoạn code sau:

var num = 8;
var num = 10;

console.log(num);

Ở đây, chúng ta khai báo biến num hai lần với từ khóa var.

  • Lần đầu tiên, num được gán giá trị 8.
  • Lần thứ hai, num được gán lại giá trị 10.

Khi chúng ta gọi console.log(num), giá trị của num sẽ là giá trị được gán cuối cùng, tức là 10.

Vì vậy, output của đoạn code trên sẽ là 10, đúng với đáp án B.

3.3. So sánh với let và const

Khác với var, từ khóa letconst trong JavaScript không cho phép khai báo lại biến với cùng tên trong cùng một phạm vi (block scope).

Nếu chúng ta thử khai báo lại biến với let hoặc const, sẽ gây ra lỗi SyntaxError.

Ví dụ:

let count = 5;
let count = 10; // Lỗi SyntaxError

const value = 3;
const value = 6; // Lỗi SyntaxError

Vì vậy, khi sử dụng letconst, chúng ta cần chú ý không khai báo lại biến với cùng tên trong cùng một phạm vi. Tuy nhiên best practice là không nên khai báo lại biến với cùng tên dù sử dụng var. Và đa số các bạn không nên dùng var nữa mà hãy sử dụng let hoặc const nhé. Chỉ sử dụng var khi bạn thật sự hiểu rõ về nó.

4. Kiểm tra sự tồn tại của key trong object và set

Output là gì?

const obj = { 1: "a", 2: "b", 3: "c" };
const set = new Set([1, 2, 3, 4, 5]);

obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);
  • A: true false false true
  • B: false true true true
  • C: true true false true
  • D: true true true true
Đá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ân tích đoạn code

Đầu tiên, chúng ta có một object obj với các keys là số và values là string. Mặc dù chúng ta không viết các keys dưới dạng string, nhưng chúng sẽ luôn được chuyển đổi thành string dưới hood.

Vì vậy, obj.hasOwnProperty('1') trả về true. Tương tự, obj.hasOwnProperty(1) cũng trả về true, vì giá trị 1 sẽ được tự động chuyển thành string.

Tiếp theo, chúng ta có một Set. Không giống như object, Set không chuyển đổi các keys thành string. Trong set của chúng ta, không có giá trị '1', nên set.has('1') trả về false. Tuy nhiên, set có chứa giá trị số 1, nên set.has(1) trả về true.

4.2. Tóm lại

Khi làm việc với object, hãy nhớ rằng các keys luôn được chuyển đổi thành string (trừ khi chúng là Symbol). Nhưng với Set, điều này không xảy ra. Đó là lý do tại sao obj.hasOwnProperty('1') trả về true, nhưng set.has('1') trả về false.

Symbol là một kiểu dữ liệu mới được giới thiệu trong ES6, nó tạo ra một giá trị không thể thay đổi và không trùng lặp được sử dụng làm key cho các thuộc tính của object. Ví dụ: const mySymbol = Symbol();. Chi tiết về Symbol mình sẽ giới thiệu trong một bài viết khác nhé.

5. Ghi đè thuộc tính trong object

Output là gì?

const obj = { a: "one", b: "two", a: "three" };
console.log(obj);
  • A: { a: "one", b: "two" }
  • B: { b: "two", a: "three" }
  • C: { a: "three", b: "two" }
  • D: SyntaxError
Đá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. Phân tích đoạn code

Trong JavaScript, khi bạn khai báo một object với các keys trùng nhau, thì key sau cùng sẽ ghi đè lên các key trước đó.

Trong ví dụ này, object obj được khai báo với hai thuộc tính a. Giá trị của thuộc tính a đầu tiên là "one", và giá trị của thuộc tính a thứ hai là "three".

Vì key a xuất hiện hai lần, nên giá trị của nó sẽ là giá trị gán cuối cùng, tức là "three". Tuy nhiên, thứ tự của các thuộc tính trong object vẫn giữ nguyên như khi chúng được khai báo.

Vì vậy, khi chúng ta log obj, kết quả sẽ là { a: "three", b: "two" }.

5.2. Tóm lại

Khi khai báo một object với các keys trùng nhau, key sau cùng sẽ ghi đè lên các key trước đó. Giá trị của key đó sẽ là giá trị gán cuối cùng, nhưng thứ tự của các thuộc tính trong object vẫn giữ nguyên như khi chúng được khai báo.

Hy vọng qua bài viết này, các bạn đã hiểu rõ hơn về cách hoạt động của JavaScript khi gặp phải các tình huống như trên. Hẹn gặp lại các bạn trong các bài viết tiếp theo của series "JavaScript Nâng Cao" nhé! 😉


All Rights Reserved

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