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ộtcomment 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. Tham chiếu và gán giá trị
Output của đoạn code sau là gì?
let person = { name: "Lydia" };
const members = [person];
person = null; console.log(members);
- A:
null
- B:
[null]
- C:
[{}]
- D:
[{ name: "Lydia" }]
Đá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. Tham chiếu trong JavaScript
Trong JavaScript, khi chúng ta làm việc với các kiểu dữ liệu phức tạp như object hoặc array, chúng ta đang làm việc với tham chiếu (references) chứ không phải giá trị trực tiếp.
Khi chúng ta khai báo:
let person = { name: "Lydia" };
Biến person
không chứa trực tiếp object { name: "Lydia" }
, mà nó chứa một tham chiếu đến vị trí trong bộ nhớ nơi object được lưu trữ.
1.2. Gán tham chiếu
Khi chúng ta thực hiện:
const members = [person];
Chúng ta đang tạo một mảng members
và đặt tham chiếu đến object person
vào vị trí đầu tiên của mảng. Lúc này, phần tử đầu tiên của members
và biến person
đều đang trỏ đến cùng một object trong bộ nhớ.
1.3. Thay đổi giá trị của biến
Khi chúng ta thực hiện:
person = null;
Chúng ta chỉ đang thay đổi giá trị của biến person
, không phải object mà nó đã trỏ tới trước đó. Biến person
giờ đây trỏ tới null
, nhưng điều này không ảnh hưởng đến object gốc trong bộ nhớ hay tham chiếu mà members
đang giữ.
1.4. Kết quả cuối cùng
Vì vậy, khi chúng ta console.log(members)
, kết quả vẫn là [{ name: "Lydia" }]
. Phần tử đầu tiên của mảng members
vẫn giữ tham chiếu đến object gốc, không bị ảnh hưởng bởi việc chúng ta gán null
cho biến person
.
Đây là một ví dụ điển hình về cách JavaScript xử lý tham chiếu và giá trị. Hiểu rõ điều này sẽ giúp bạn tránh được nhiều lỗi phổ biến khi làm việc với objects và arrays trong JavaScript.
2. Vòng lặp for...in trong JavaScript
Output của đoạn code sau là gì?
const person = { name: "Lydia", age: 21
}; for (const item in person) { console.log(item);
}
- A:
{ name: "Lydia" }, { age: 21 }
- B:
"name", "age"
- C:
"Lydia", 21
- D:
["name", "Lydia"], ["age", 21]
Đá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. Vòng lặp for...in
Vòng lặp for...in
trong JavaScript được sử dụng để lặp qua các thuộc tính có thể đếm được (enumerable properties) của một object. Điều quan trọng cần nhớ là vòng lặp này sẽ lặp qua các keys của object, không phải values.
2.2. Cách hoạt động của for...in
Trong mỗi lần lặp, biến item
sẽ nhận giá trị là tên của thuộc tính (key) trong object person
. Trong trường hợp này, object person
có hai thuộc tính: name
và age
.
2.3. Phân tích kết quả
Khi chạy vòng lặp:
- Trong lần lặp đầu tiên,
item
sẽ là"name"
. - Trong lần lặp thứ hai,
item
sẽ là"age"
.
Do đó, khi chúng ta console.log(item)
trong mỗi lần lặp, kết quả sẽ in ra "name"
và "age"
.
2.4. Lưu ý quan trọng
Điều quan trọng cần nhớ là for...in
chỉ lặp qua các keys của object, không phải values. Nếu bạn muốn truy cập values, bạn cần sử dụng person[item]
trong vòng lặp.
Ví dụ, nếu bạn muốn in ra cả keys và values, bạn có thể làm như sau:
for (const item in person) { console.log(item + ": " + person[item]);
}
Kết quả sẽ là:
name: Lydia
age: 21
Hiểu rõ cách hoạt động của vòng lặp for...in
sẽ giúp bạn làm việc hiệu quả hơn với objects trong JavaScript, đặc biệt khi bạn cần duyệt qua tất cả các thuộc tính của một object.
3. Phép cộng và ép kiểu trong JavaScript
Output của đoạn code sau là gì?
console.log(3 + 4 + "5");
- A:
"345"
- B:
"75"
- C:
12
- D:
"12"
Đá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. Thứ tự thực hiện phép toán
Trong JavaScript, các phép toán được thực hiện từ trái sang phải. Điều này có nghĩa là trình thông dịch sẽ xử lý biểu thức theo thứ tự xuất hiện của các phép toán.
3.2. Phép cộng số học
Đầu tiên, JavaScript sẽ thực hiện phép cộng giữa hai số:
3 + 4 = 7
Sau bước này, biểu thức của chúng ta trở thành:
7 + "5"
3.3. Ép kiểu trong JavaScript
Khi JavaScript gặp phép cộng giữa một số và một chuỗi, nó sẽ thực hiện ép kiểu. Trong trường hợp này, số 7 sẽ được chuyển đổi thành chuỗi "7".
3.4. Nối chuỗi
Sau khi ép kiểu, phép toán trở thành phép nối chuỗi:
"7" + "5" = "75"
3.5. Kết quả cuối cùng
Vì vậy, kết quả cuối cùng của biểu thức 3 + 4 + "5"
là chuỗi "75"
.
3.6. Lưu ý quan trọng
Một đặc điểm quan trọng của JavaScript: ép kiểu ngầm (implicit type coercion). JavaScript sẽ tự động chuyển đổi kiểu dữ liệu khi cần thiết để thực hiện phép toán.
Nếu bạn muốn tránh ép kiểu và đảm bảo rằng tất cả các phép toán đều là phép cộng số học, bạn có thể sử dụng dấu ngoặc đơn để chỉ định thứ tự thực hiện:
console.log((3 + 4) + "5"); // Kết quả: "75"
console.log(3 + 4 + +"5"); // Kết quả: 12 (dấu + trước "5" chuyển chuỗi "5" thành số 5)
Hiểu rõ về cách JavaScript xử lý các phép toán và ép kiểu sẽ giúp bạn tránh được nhiều lỗi không mong muốn trong quá trình lập trình.
4. Phân tích cú pháp số trong JavaScript
Giá trị của num
là gì?
const num = parseInt("7*6", 10);
- A:
42
- B:
"42"
- C:
7
- 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é ❓️
4.1. Hàm parseInt() trong JavaScript
Hàm parseInt()
trong JavaScript được sử dụng để phân tích một chuỗi và trả về một số nguyên. Cú pháp của hàm này là:
parseInt(string, radix)
Trong đó:
string
là chuỗi cần phân tíchradix
là cơ số của hệ đếm (từ 2 đến 36, mặc định là 10 nếu không được chỉ định)
4.2. Cách hoạt động của parseInt()
Khi phân tích chuỗi, parseInt()
sẽ:
- Bỏ qua khoảng trắng ở đầu chuỗi.
- Phân tích các ký tự tiếp theo cho đến khi gặp ký tự không hợp lệ.
- Nếu không tìm thấy số hợp lệ, trả về
NaN
. - Nếu tìm thấy số hợp lệ, chuyển đổi phần hợp lệ đó thành số nguyên.
4.3. Phân tích kết quả
Trong trường hợp này:
const num = parseInt("7*6", 10);
- Chuỗi đầu vào là "7*6"
- Cơ số là 10 (hệ thập phân)
parseInt()
sẽ bắt đầu phân tích chuỗi từ trái sang phải:
- Ký tự '7' là hợp lệ trong hệ thập phân.
- Ký tự '*' không hợp lệ trong hệ thập phân.
Do đó, parseInt()
sẽ dừng lại tại ký tự '*' và trả về giá trị 7.
4.4. Lưu ý quan trọng
Điều quan trọng cần nhớ là parseInt()
sẽ chỉ chuyển đổi phần đầu của chuỗi nếu nó là một số hợp lệ. Nó sẽ bỏ qua bất kỳ ký tự nào sau ký tự không hợp lệ đầu tiên.
Ví dụ:
console.log(parseInt("10.5", 10)); // Kết quả: 10
console.log(parseInt("10px", 10)); // Kết quả: 10
console.log(parseInt("a10", 10)); // Kết quả: NaN
Hiểu rõ cách hoạt động của parseInt()
sẽ giúp bạn xử lý chuyển đổi chuỗi sang số một cách chính xác và tránh được các lỗi không mong muốn trong quá trình lập trình JavaScript.
5. Map và undefined
trong JavaScript
Output của đoạn code sau là gì?
[1, 2, 3].map(num => { if (typeof num === "number") return; return num * 2;
});
- A:
[]
- B:
[null, null, null]
- C:
[undefined, undefined, undefined]
- D:
[ 3 x empty ]
Đá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ương thức map() trong JavaScript
Phương thức map()
trong JavaScript được sử dụng để tạo một mảng mới bằng cách gọi một hàm cho mỗi phần tử trong mảng gốc. Hàm này nhận ba tham số: phần tử hiện tại, chỉ số của phần tử, và mảng gốc.
5.2. Cách hoạt động của map()
Khi sử dụng map()
, một mảng mới sẽ được tạo ra với cùng số lượng phần tử như mảng gốc. Giá trị của mỗi phần tử trong mảng mới sẽ là kết quả trả về từ hàm callback được truyền vào map()
.
5.3. Phân tích đoạn code
Trong đoạn code này:
[1, 2, 3].map(num => { if (typeof num === "number") return; return num * 2;
});
Hàm callback kiểm tra xem num
có phải là số hay không. Nếu là số, hàm sẽ return ngay lập tức mà không có giá trị nào được chỉ định (tương đương với return undefined
). Nếu không phải số, hàm sẽ trả về num * 2
.
5.4. Kết quả
Trong trường hợp này:
- Tất cả các phần tử (1, 2, 3) đều là số.
- Điều kiện
typeof num === "number"
luôn đúng cho mọi phần tử. - Hàm callback luôn return ngay lập tức mà không có giá trị được chỉ định.
Khi một hàm trong JavaScript return mà không chỉ định giá trị, nó sẽ ngầm định trả về undefined
.
Do đó, kết quả cuối cùng sẽ là một mảng mới với ba phần tử, tất cả đều là undefined
:
[undefined, undefined, undefined]
5.5. Lưu ý quan trọng
Điều quan trọng cần nhớ là map()
luôn trả về một mảng mới có cùng số lượng phần tử với mảng gốc. Nếu hàm callback không trả về giá trị nào (hoặc trả về undefined
), phần tử tương ứng trong mảng mới sẽ là undefined
.
Ví dụ khác để minh họa:
const result = [1, 2, 3].map(num => { if (num % 2 === 0) return num * 2; // Không có return statement cho số lẻ
});
console.log(result); // [undefined, 4, undefined]
Hiểu rõ cách hoạt động của map()
và cách JavaScript xử lý các giá trị trả về từ hàm sẽ giúp bạn tránh được nhiều lỗi không mong muốn khi làm việc với mảng trong JavaScript.