Những câu hỏi hay trong Javascript Phần 1
Giới thiệu
Javascript (JS) là ngôn ngữ lập trình bậc cao được sử dụng rộng rãi cho lập trình web ở cả phía người dùng (client) lẫn phía máy chủ (backend). Việc nắm được kiến thức, các cấu trúc, cú pháp của javascript là một lợi thế rất lớn cho những ai đang muốn kiếm cơ hội việc làm liên quan đến lĩnh vực này. Để xây dựng sự nghiệp với lập trình JS, ứng viên cần phải vượt qua những kỳ phỏng vấn của nhà tuyển dụng với rất nhiều câu hỏi khó dễ khác nhau.
Dưới đây là danh sách một số câu hỏi và trả lời thường gặp trong các kỳ phỏng vấn JS được tác giả sưu tầm. Trong một kỳ phỏng vấn, bạn có thể gặp rất nhiều câu hỏi dễ, nhưng cũng có thể gặp rất nhiều câu khó, nó tùy thuộc vào vị trí ứng tuyển cũng như kinh nghiệm của bạn, ngoài ra nó còn tùy thuộc vào nhu cầu nhà tuyển dụng, số lượng người tham dự phỏng vấn. Hi vọng một số câu hỏi và trả lời dưới đây sẽ giúp ích cho bạn trong kỳ phỏng vấn tới, hoặc giúp bạn tham khảo trong quá trình xây dựng ứng dụng và hiểu rõ hơn về JS.
1. Javascript là gì?
Javascript là ngôn ngữ kịch bản (scripting) được viết cho cả front-end (client-side) và back-end (server-side) có thể được chèn vào trong trang HTML và được biên dịch trên trình duyệt (browser), sau này từ khi NodeJS ra đời nó còn có thể biên dịch ngoài ngoài trình duyệt. JS cũng là ngôn ngữ lập trình hướng đối tượng (OOP).
2. Trong JS có bao nhiêu kiểu dữ liệu?
- Number
- String
- Boolean
- Object
- Function
- Undefined
3. Công dụng của hàm isNaN là gì?
Kiểm tra xem tham số đưa vào trong hàm có phải dạng Number hay không, nếu là dạng Number sẽ trả về false
, ngược lại là true
. VD:
let a = 5 ; let b = "năm"; isNan(a) // false isNan(b) //true
4. Sự khác biệt giữa Java vs JS?
Java | Javascript | ||
---|---|---|---|
Ngôn ngữ lập trình hướng đối tượng, khởi đầu được phát triển bởi James Gosling of Sun Microsystem, sau này là Oracle | Ngôn ngữ kịch bản hướng client-side được phát triển bởi Netscape, ban đầu được biết đến là Livescript | ||
Ngôn ngữ độc lập được thực thi bằng JVM (Java virtual machine) nên cần được biên dịch thành "byte-code" và đuôi file có dang .java | Phải được đặt trong một HTML document để trình duyệt có thể thực thi chương trình qua các hàm | ||
Hoạt động dựa trên hướng đối tượng (OOP) và trong Java không thể có bất kỳ chương trình nào tồn tại mà không được tạo ra từ class | Làm việc dựa trên khuôn mẫu prototype | ||
Để viết code trong java cần phải cài JDK (Java development Kit) | Để viết chương trình JS cần một text editor | ||
Ngôn ngữ có kiểu tĩnh ( Khai báo tường minh kích thước dữ liệu trước khi thực thi chương trình) | Ngôn ngữ kiểu động ( trong quá trình thực thi, nó có thể mở rộng kích thước dữ liệu) | ||
Mã nguồn được ẩn đi vì nó được biên dịch thành kiểu mã không thể đọc | Mã nguồn có thể đọc dễ dàng bởi bất kỳ ai vì nó được viết dưới dạng văn bản thuần | ||
Java được phát triển bằng việc giữ "bảo mật cho code" bởi vì mục đích chính của nó là lưu trữ qua JVM. | Javascript bảo mật kém hơn và trong quá trình phát triển web, các developer thường cố gắng ngăn chặn các cuộc tấn công bằng XSS (cross-site-scripting), ... | ||
Đa luồng ( Thực hiện một lúc nhiều công việc) | Đơn luồng ( chỉ thực hiện một công việc tại một thời điểm, thực hiện xong mới qua công việc khác ) |
5. Negative Infinity là gì ?
Negative Infinity là một số trong JavaScript có thể được lấy bằng cách chia số âm cho số không.
6.Có thể chia mã JavaScript thành nhiều dòng không?
Việc ngắt trong câu lệnh chuỗi có thể được thực hiện bằng cách sử dụng dấu gạch chéo ngược, \
, ở cuối dòng đầu tiên
document.write("This is \a program");
Và nếu bạn thay đổi thành một dòng mới khi không nằm trong một câu lệnh chuỗi, thì javaScript sẽ bỏ qua ngắt dòng.
var x=1, y=2, z= x+y;
Đoạn mã trên hoàn toàn ổn, mặc dù không nên vì nó cản trở việc gỡ lỗi.
7. Biến không khai báo và không xác định là gì?
- Biến không khai báo là biến không tồn tại trong chương trình. Nếu chương trình cố gắng đọc biến không được khải báo sẽ gặp phải lỗi tham chiếu.
- Biến không xác định (undefined variables) là những biến đã được khai báo nhưng không được gán giá trị cho nó. Nếu chương đọc biến đó, nó sẽ trả về giá trị
undefined
8. Viết code để thêm động các phần tử mới
<html> <head> <title>t1</title> </head> <body> <p id="firstP">firstP<p> <script type="text/javascript"> function addNode() { var newP = document.createElement("p"); var textNode = document.createTextNode(" This is a new text node"); newP.appendChild(textNode); document.getElementById("firstP").appendChild(newP); } </script> </body> </html>
9. Hoisting trong javascript ?
Xét VD :
function sayHi() { console.log(name); console.log(age); var name = 'Lydia'; let age = 21; } sayHi();
Kết quả là :
undefined Reference Error
Bên trong hàm, đầu tiên ta khai báo biến name
với từ khóa var
nghĩa là biến đã được hoisted ( không gian bộ nhớ được thiết lập trong giai đoạn khởi tạo) với giá trị mặc định là undefined
, đến khi chương trình thực thi chạy đến dòng code định nghĩa cho biến đó thì nó mới thực sự nhận giá trị như mong muốn.
Trước khi cố gắng console.log(name)
, chúng ta chưa hề định nghĩa biến nào là name
cả, vì hoisting xảy ra và biến name
được gán giá trị mặc định undefined
Các biến với từ khóa let
(và const
) cũng được hoisting, nhưng không giống như từ khóa var
, chúng không thể truy cập trước khi chúng thực sự được khởi tạo.
Đây được gọi là Vùng chết tạm thời. Do đó, khi cố gắng truy cập các biến này trước khi được khai báo. Javascript sẽ ném ra ReferenceError
.
10. Vòng lặp sử dụng var
hoặc let
trong Javascript có gì khác nhau?
Để trả lời câu hỏi này thì bạn thử đoán kết quả của chương trình Javacript sau đây là gì?
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); } for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); }
- A:
0 1 2
and0 1 2
- B:
0 1 2
and3 3 3
- C:
3 3 3
and0 1 2
Bởi vì hàng chờ sự kiện trong JS, hàm callback function setTimeout()
được gọi sau khi vòng lặp thực thi.
Do biến i
được khai báo bằng từ khóa var
, nó sẽ được gọi ngay khi chương trình khởi tạo và biến này trở thành biến toàn cục. Trong quá trình lặp, i
tăng 1 đơn vị sau mỗi lần lặp. Nhưng trước khi hàm callback setTimeout()
được gọi, thì i đã lặp đến lần thứ 3 như trong vòng lặp thứ nhất.
Ở vòng lặp thứ 2, biến i
được khai báo với từ khóa let
. Những biến được khai báo theo từ khóa let
(hoặc const
) có phạm vi trong một khối lệnh ( block-scope có thể hiểu bắt đầu từ {
và kết thúc trong }
). Do đó, trong qúa trình lặp, i
sẽ có giá trị mới, và mỗi giá trị đó đều nằm trong phạm vi của vòng lặp.
11. Khi sử dụng từ khóa this
trong arrow function
nên lưu ý điều gì?
Xét VD sau :
const rectangle = { length : 10, width : 5, area(){ return this.length * this.width }, perimeter : () => 2 * (this.length + this.width) } console.log(rectangle.area()); console.log(rectangle.perimeter());
- A: 50 and 30
- B: 50 and NaN
- C: NaN and NaN
- D: NaN and 30
Lưu ý rằng hàm area()
là một regular function
(một hàm thông thường), còn perimeter()
là một arrow function
.
Không giống như các hàm thông thường, arrow function
khi sử dụng từ khóa this
là nó chỉ phạm vi chỉ trong arrow function
đó thôi. Trong khi ta muốn lấy length
và width
từ rectangle
tức là scope nằm ngoài arrow function này, nên từ khóa this
ở đây không trỏ đúng như mong muốn.
Kết quả là không có giá trị area
trên đối tượng đó, nó trả về undefined
.
12. Chuyển đổi số bằng toán tử +, tính chất truthy value
Xét VD sau:
+true; !"John Doe"
- A: 1 and false
- B: false and NaN
- C: false and false
Toán tử +
giúp chuyển đổi một toán hàng thành một số, true
là 1
, false
là 0
Do trong JS có một vài kiểu :
- Kiểu
Boolean
có falsy value làfalse
- Kiểu
Number
co falsy value là0
vàNaN
- Kiểu
String
có falsy value là "" - Kiểu null có falsy value là
null
- Kiểu undefined có falsy value là
undefined
Vậy chúng ta có tổng cộng là 6 falsy value
. Các giá trị còn lại đều là truthy value
.
Chính vì thế chuỗi 'John Doe' là một truthy value. Do đó phủ định của truthy value. Dĩ nhiên kết quả sẽ trả về giá trị là false
.
13. Sự khác nhau khi sử dụng dấu ngoặc [ ]
và toán tử dot để truy cập Object là gì?
Xét VD sau :
const bird = { size: "small" }; const mouse = { name: "Mickey", small: true };
- A:
mouse.bird.size
không hợp lệ - B:
mouse[bird.size]
không hợp lệ - C:
mouse[bird["size"]]
không hợp lệ - D: Tất cả đều hợp lệ
Trong JS, tất cả các object key đều là chuỗi (trừ khi đó là Symbol
). Mặc dù ta không gõ chúng dưới dạng chuỗi nhưng khi biên dịch, máy tính sẽ chuyển chúng thành chuỗi.
Javascript thông dịch các statement. Khi ta sử dụng ký hiệu []
, nó sẽ thấy dấu [
và tiếp tục tìm cho đến khi thấy dấu ]
. Sau đó, nó sẽ đánh giá statement đó.
Trong mouse[bird.size]
: đầu tiên nó sẽ đánh giá bird.size
có giá trị là small
, từ đó suy ra mouse["small"]
và quả thực trong object mouse
có thuộc tính small
và trả về giá trị true
. Tuy nhiên, với việc sử dụng dấu .
thì hành vi này không xảy ra. Object mouse
không có thuộc tính nào là bird
, điều này dẫn đến mouse.bird
có giá trị undefined
.
Sau đó, chúng ta gọi đến size
cũng bằng cách sử dụng toán tử dot .
là: mouse.bird.size
. Từ mouse.bird
đã là undefined
thì chúng ta đang thực sự gọi là undefined.size
. Dẫn tới, điều này là không hợp lệ, lúc này bạn sẽ nhận được một lỗi như là: Cannot read property "size" of undefined
14. Tương tác bằng tham chiếu trong Javascript là thế nào?
Xét VD sau :
let c = { greeting: 'Hey!' }; let d; d = c; c.greeting = 'Hello'; console.log(d.greeting);
- A: Hello
- B: Hey!
- C: undefined
- D: ReferenceError
- E: TypeError
Trong Javascript, tất cả các object đều tương tác với nhau thông qua tham chiếu (referrence) khi thiết lập chúng bằng nhau.
Đầu tiên, biến c
giữ một object. Sau đó, chúng ta gán d
với tham chiếu c
(đang giữ một object)
Khi chúng ta thay đổi object đồng nghĩa với việc thay đổi tất cả chúng.
15. Toán tử ==
và ===
trong Javascript khác nhau như thế nào?
Xét VD sau :
let a = 3; let b = new Number(3); let c = 3; console.log(a == b); console.log(a === b); console.log(b === c);
- A: true false true
- B: false false true
- C: true false false
- D: false true true
new Number()
là một constructor function được xây dựng sẵn trong JS. Mặc dù nó trông giống như một số nhưng kiểu thực sự của nó là một object
.
Khi chúng ta sử dụng toán tử ==
, nó chỉ kiểm tra xem có cùng giá trị hay không. Nếu cả 2 đều có giá trị là 3, thì nó sẽ trả về true
Tuy nhiên, khi sử dụng toán tử ===
, nó sẽ kiểm tra cả giá trị lẫn kiểu biến có giống nhau hay không. Ở đây, mặc dù cả 2 biến a
và b
, hoặc c
và b
đều cùng giá trị là 3
nhưng a
và c
có kiểu là Number
, còn b
có kiểu là object
, vì vậy giá trị sẽ là false
.
16. Hàm static trong Javascript
Xét VD sau :
class Chameleon { static colorChange(newColor) { this.newColor = newColor; return this.newColor; } constructor({ newColor = "green" } = {}) { this.newColor = newColor; } } const freddie = new Chameleon({ newColor: "purple" }); freddie.colorChange("orange");
- A: orange
- B: purple
- C: green
- D: TypeError
Hàm colorChange()
ở dạng static
. Cũng giống như trong OOP, static
không thể dùng cho khai báo instance
(các biến được khởi tạo bằng từ khóa new với tên Object) mà chỉ được thiết kế cho constructor mà chúng tạo ra.
Vì freddie
là một instance
, do đó hàm static
không thể truyền cho nó, và không có sẵn trên freddie
nên sẽ trả về TypeError
.
17. "use strict" dùng để làm gì?
Xét VD sau :
let greeting; greetign = {}; // lỗi nhập sai console.log(greetign);
- A: {}
- B: ReferenceError: greetign is not defined
- C: undefined
Nó sẽ log ra một object vì chúng ta đã tạo ra một object rỗng trên global object. Khi gõ lỗi greeting
thành greetign
, trình thông dình của JS thực sự hiểu tình huống này như là global.greetign = {}
( hoặc window.greetign = {}
nếu ở trình duyệt)
Để tránh điều này, chúng ta cần sử dụng use strict
. Cách này đảm bảo bạn phải khai báo biến trước khi gán giá trị cho nó.
18. Điều gì xảy ra khi tạo thêm thuộc tính cho hàm giống như object?
Xét VD sau :
function user(){ cosole.log("John Doe"); } user.age = 25; }
- A: Không có gì.
- B: SyntaxError. You cannot add properties to a function this way.
- C: undefined
- D: ReferenceError
Điều này hoàn toàn bình thường trong javascript, vì các hàm trong JS là các object (Tất cả mọi thứ ngoài các kiểu nguyên thủy đều là các object)
Một hàm là một object đặc biệt. Code bạn tự viết không phải là hàm thực tế. Hàm là một object có thuộc tính và thuộc tính này bất khả xâm phạm.
19. Thêm một thuộc tính vào constructor thì chuyện gì sẽ xảy ra?
Xét VD sau :
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } const member = new Person('Lydia', 'Hallie'); Person.getFullName = function() { return `${this.firstName} ${this.lastName}`; }; console.log(member.getFullName());
- A: TypeError
- B: SyntaxError
- C: Lydia Hallie
- D: undefined undefined
Bạn không thể thêm thuộc tính vào hàm constructor như bạn có thể với các object thông thường.
Nếu bạn muốn thêm một hàm cho tất cả các object cùng một lúc, bạn cần sử dụng prototype
để thay thế. Vì vậy trong trường hợp này, để khắc phục lỗi trên, bạn sửa như sau :
Person.prototype.getFullName = function(){ return `${this.firstName} ${this.lastName}`; }
Bằng cách này thì member.getFullName()
sẽ làm việc. Why?
Giả sử nếu chúng ta thêm phương thức này vào chính hàm constructor
. Có lẽ không phải Person
nào cũng cần dùng đến phương thức này.
Điều này sẽ gây lãng phí bộ nhớ, vì lúc này thuộc tính sẽ được lưu cho mỗi instance khởi tạo từ Person
.
Thay vào đó, nếu chúng ta chỉ sử dụng prototype
, lúc đó chúng ta chỉ cần lưu có một vị trí trong bộ nhớ, nhứng tất cả cacinstance khởi tạo từ
Person` đều có quyền truy cập vào nó.
20. Khởi tạo đối tượng có từ khóa new và không có từ khóa new khác gì nhau?
Xét VD sau :
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } const lydia = new Person('Lydia', 'Hallie'); const sarah = Person('Sarah', 'Smith'); console.log(lydia); console.log(sarah);
- A: Person {firstName: "Lydia", lastName: "Hallie"} and undefined
- B: Person {firstName: "Lydia", lastName: "Hallie"} and Person {firstName: "Sarah", lastName: "Smith"}
- C: Person {firstName: "Lydia", lastName: "Hallie"} and {}
- D: Person {firstName: "Lydia", lastName: "Hallie"} and ReferenceError
Đối với sarah
, chúng ta không sử từ khóa new
. Khi sử dụng từ khóa new
, nó sẽ tham chiếu đến một object rỗng mới mà chúng ta tạo ra. Tuy nhiên, nếu không sử dụng từ khóa new
, this
trong object sẽ tham chiếu đến global object
.
Khi nói rằng this.firstName
sẽ tương đương với "Sarah" và this.lastName
sẽ bằng "Smith". Thực sự this
của sarah
đang được hiểu là global.firstName = 'Sarah'
và global.lastName = 'Smith'
.
Chính sahra
là undefined
, vì chúng ta không trả về giá trị từ hàm Person
.
21. 3 Giai đoạn của event propagation là gì?
- A: Target > Capturing > Bubbling
- B: Bubbling > Target > Capturing
- C: Target > Bubbling > Capturing
- D: Capturing > Target > Bubbling
Tron giai đoạn Capturing, các sự kiện đi qua phần tử đầu tiên xuống phần tử mục tiêu. Sau đó nó target vào phần tử mục tiêu và Bubbling bắt đầu.
22. Tất cả các object đều có prototype?
- A : true
- B : false
Tất cả các object đều có prototype, ngoại trừ base object. Base object có quyền truy cập đến một số phương thức và thuộc tính, chẳng hạn như .toString()
.
Đây là lý do tại sao bạn có thể sử dụng các phương thức được JS tích hợp sẵn, tất cả các phương thức như vậy có sẵn trên prototype
.
Mặc dù JS không thể tìm thấy nó trực tiếp trên object của bạn, nhưng nó sẽ đi xuống prototype chain và tìm thấy nó ở đó, điều này giúp bạn có thể truy cập nó.
23. Cộng 1 số với một chuỗi thì ra kết quả gì?
Xét VD sau :
function sum(a, b){ return a + b ; } sum(1,"2");
- A: NaN
- B: TypeError
- C: "12"
- D: 3
Javascript là ngôn ngữ lập trình động nghĩa là chúng ta không cần chỉ định kiểu biến cụ thể cho biến đó là gì. Những giá trị đó có thể tự động được chuyển thành kiểu khác mà bạn chưa biết, điều này được gọi là ép kiểu ngầm cho biến (implicit type coercion). Ép kiểu là chuyển từ kiểu này sang kiểu khác.
Trong ví dụ trên, JS chuyển số 1
thành kiểu string
để hàm có ý nghĩa và trả về một giá trị. Trong quá trình cộng kiểu number(1) và kiểu string("2")
, number đã được coi là một string.
Vì vậy khi sử dụng toán tử + để cộng hai chuỗi lại với nhau như '1' + '2' sẽ trả về kết quả là '12'.
24. Sự khác biệt của number++ và ++number là gì?
let number = 0; console.log(number++); console.log(++number); console.log(number);
- A: 1 1 2
- B: 1 2 2
- C: 0 2 2
- D: 0 1 2
Toán tử ++ (number++) được thêm vào hậu tố hoạt động như sau : - Đầu tiên nó trả về giá trị - Sau đó nó tăng lên 1 đơn vị
Toán tử ++ (++number) được thêm vào tiền tố hoạt động như sau: - Đầu tiên tăng lên 1 đơn vị - Sau đó trả về giá trị.
25.Nội suy chuỗi với Tagg Template Literal trong ES6 là gì?
Xét VD sau :
function getPersonInfo(one, two, three) { console.log(one); console.log(two); console.log(three); } const person = "Lydia"; const age = 21; getPersonInfo`${person} is ${age} years old`;
- A:
Lydia
21
["", " is ", " years old"]
- B:
["", " is ", " years old"]
Lydia
21
- C:
Lydia
["", " is ", " years old"]
21
Nếu bạn sử dụng tagg template literal, giá trị của đối số đầu tiên luôn là một mảng của các giá trị chuỗi. Các đối số còn lại nhận được các giá trị của các biểu thức được truyền qua!
Phần 1 của Series Những câu hỏi hay trong Javascript đến đây là hết, rất cám ơn sự theo dõi của các bạn, hẹn gặp lại các bạn trong phần 2. Nếu bạn thích hãy like, comment ở phần bình luận bên dưới, cũng như chia sẻ bài viết để ủng hộ tác giả, rất cám ơn bạn.