50++ Câu Hỏi Phỏng Vấn JavaScript Cùng Hướng Dẫn Trả Lời

Hãy cùng ITguru thử sức với 50 câu hỏi phỏng vấn JavaScript được chọn lọc từ nhiều nguồn khác nhau. Các câu hỏi này ở nhiều cấp độ khác nhau, từ cơ bản đến nâng cao.  Nếu bạn muốn kiểm tra lại kiến thức của mình hoặc chuẩn bị cho một cuộc phỏng vấn JavaScript, thì bài viết này dành cho bạn.

Ngoài những câu hỏi trong bài, bạn cũng có thể tìm được một số nguồn câu hỏi ở cuối bài, giúp bạn chuẩn bị tốt hơn cho chặng đường sắp tới

Các câu hỏi ở cấp độ dễ

Q1: Giải thích phép so sánh trong JavaScript

Trong Java, chúng ta có hai cách so sánh: so sánh nghiêm ngặt (strict comparison) và chuyển đổi kiểu (type-converting).1/ strict comparison

 Strict comparison sử dụng ba dấu bằng ===Cả kiểu và giá trị phải giống nhau, ví dụ:

10 === 10 // true 'hello world' === 'hello world' // true because both are strings and equal values 23 === '23' // false because you are comparing a Number to a String

Strict comparison không cho phép ép kiểu dữ liệu  (coercion).

Vì một số bạn có thể không quen với khái niệm coercion, chúng ta sẽ xem một ví dụ nhanh dưới đây:Trong JS, coercion  là sự chuyển đổi giữa hai kiểu dữ liệu được tạo sẵn. Có hai loại coercion:

  • Explicit coercion (ép kiểu tường minh):
let a = '23'; let b = Number(a); a; // '23' b; // 23 (this is the Number 23 not the String)
  • Implicit coercion (ép kiểu ngầm định):
let a = '23'; let b = a * 2; // '23' is implicitly coerced to 23 a; // '23' b; // 46 (this is the Number 46 not a String)

2/ So sánh trừu tượng (Abstract comparison )

So sánh trừ tượng sử dụng Dấu bằng kép (Double Equals) ==So sánh trừu tượng là so sánh giá trị bình đẳng và được phép ép kiểu dữ liệu (coercion) . Vì vậy, hai giá trị được so sánh sau khi chuyển đổi chúng thành cùng một kiểu dữ liệu.

23 == '23' // true

Các toán tử trong JavaScript

Các toán tử trong JavaScript

Q2: Callback function là gì và hãy cho một ví dụ đơn giản:

Callback function (hàm gọi lại) là một hàm được thực thi sau khi một hàm khác đã thực thi xong. Hay nói cách khác, một callback function là một hàm được truyền cho một hàm khác như một đối số và được thực thi sau khi một số tác vụ đã hoàn thành. Ví dụ:

function writeBlog(topic, callback) { alert(`Starting my ${topic} blog.`); // then execute the callback function that was passed callback(); } writeBlog('JS', function() { alert('Finished my blog!'); });

Nếu bạn chạy đoạn code, bạn sẽ nhận được hai thông báo. Thông báo đầu tiên “Starting my JS blog.” và thông báo thứ hai: “Finished my blog!”

Xem thêm: JavaScript Callback

Q3: Primitive là gì? Các primitive value trong JavaScript là gì?

Trong JavaScript có 2 loại kiểu dữ liệu: Kiểu nguyên thủy (primitive) và object.

Một kiểu nguyên thủy không phải là một object và không có các phương thức (method) của riêng nó. Tất cả các kiểu nguyên thủy là bất biến. Có sáu loại nguyên thủy trong JS:

  • Boolean — true or false
  • Null — no value
  • Undefined — một biến được khai báo nhưng chưa được cung cấp giá trị
  • Number — integers, floats..
  • String — tất cả những gì bên trong dấu “ ”
  • Symbol — một giá trị duy nhất không bằng bất kỳ giá trị nào khác (được giới thiệu trong ES6)
Symbol('x') === Symbol('x') // false

Q4: Toán tử && để làm gì?

Hãy xem đoạn code này

Toán tử && trong câu hỏi phỏng vấn JavaScript

Về cơ bản, toán tử && (đọc là logic AND) tìm giá trị  (falsy) đầu tiên trong biểu thức và trả về giá trị đó. Nếu nó không tìm thấy giá trị falsy, nó sẽ trả về giá trị cuối cùng trong biểu thức. Vì vậy, dòng đầu tiên:

console.log([] && "true" && 1);

trả về false vì false là giá trị falsy đầu tiên trong biểu thức đó. Dòng thứ hai:

console.log([] && "true" && 1);

không chứa giá trị falsy và do đó trả về giá trị cuối cùng trong biểu thức, là 1.

Q5: Toán tử || dùng để làm gì trong JavaScript

Toán tử || là toán tử OR. Nó tìm thấy biểu thức truthy đầu tiên và trả về biểu thức đó. Nó cũng sử dụng tính năng short-circuiting để tránh những lần kiểm tra không cần thiết. Do đó, sẽ rất hữu ích khi đặt giá trị mặc định của một biến trong trường hợp những giá trị trước lựa chọn mặc định là falsyVí dụ, nếu chúng ta có:

let foo = null || undefined || 'foo';

Và  ta có ‘foo’ là giá trị của foo.

Q6: Hãy cho biết Falsy value và Truthy value là gì? Có những falsy value nào trong JavaScript?

Đây là câu hỏi phỏng vấn JavaScript thường được hỏi và nhiều bạn khá bối rối.

Falsy values là những giá trị trong Javascript mà khi ép kiểu về Boolean, thì sẽ cho ra giá trị false. Tương tự, truthy values là những giá trị mà khi ép kiểu về Boolean, thì sẽ cho ra giá trị true.

Có 7 falsy values trong JavaScript:

  • number 0
  • BigInt 0n
  • keyword undefined
  • keyword null
  • number NaN
  • boolean false
  • empty string “” (tương ứng với `` hay ‘’)

Tất cả những kiểu dữ liệu trên sẽ thành false trong các điều kiện. Lưu ý nhiều tài liệu sẽ chỉ liệt kê 6 falsy value và quên 0n. Ngoài ra, đối lập với falsy value là các truthy value. Ví dụ: {} và [] là truthy.

Q7: Sự khác biệt giữa undefined và null là gì?

Đây là một khái niệm có thể gây nhầm lẫn cho những người mới làm quen với JavaScript và là câu hỏi phỏng vấn có thể làm bạn mất điểm.

1/Null

Null có nghĩa là một giá trị rỗng hoặc không tồn tại. Null được gán cho một biến như là một đại diện không có giá trị:

var test1 = null; console.log(test1); // null

null cũng là một object. Thật thú vị, JS lại xem kiểu của null là một đối tượng trong khi lẽ ra kiểu của null phải là null:

console.log(typeof test1); // object

2/ Undefined

Undefined có nghĩa là một biến đã được khai báo, nhưng giá trị của biến đó vẫn chưa được xác định. Ví dụ:

var test2; console.log(test2); // undefined

Không giống như null, undefined thuộc loại undefined:

console.log(typeof test2); // undefined

Xem thêm:  JavaScript — What’s the difference between Null & Undefined?

Câu hỏi có độ khó trung bình

Q8: IIFEs (Immediately Invoked Function Expressions) là gì?

Một Immediately Invoked Function Express  được thực thi ngay sau khi nó được tạo.

(function iifeExample() { console.log('Hello, World!'); })(); // Hello, World!

Q9: Hãy phân biệt giữa anonymous và named functions?

Anonymous function (Hàm ẩn danh) chỉ đơn giản là một hàm không có tên và tất cả đều được tạo trong thời gian chạy ứng dụng.Named function (hàm được đặt tên)  khá hữu ích khi gỡ lỗi và tìm kiếm những hàm nào gây ra lỗi.

function foo(){ alert("Hi, I'm foo!"); } var bar = function(){ alert("I am an anonymous function that is assigned to the variable "bar"); } foo(); // "Hi, I'm foo!" bar(); //"I am an anonymous function that is assigned to the variable "bar"

Q10: Làm thế nào bạn có thể kiểm tra xem một số có phải là số nguyên hay không?

Một cách nhanh chóng sẽ là dùng hàm isInteger . Tuy nhiên hàm này không cho biết đó là số nguyên dương hay số nguyên âm.

Một cách khác là lấy số đo chia số cho 1 và xem có phần dư hay không.

function isAnInt(num) { return num % 1 === 0; } console.log(isInt(10)); // true console.log(isInt(1.1)); // false console.log(isInt(0.5)); // false

Q11: Hãy phân tích các phần khác nhau của URL này https://www.movies.com:443/movies/index.html?type=horror&imdb=8#Blockbusters

Các phần của URL(Uniform Resource Locator) như sau:

  • Phần đầu tiên của URL, http hoặc https, được gọi là giao thức. Giao thức xác định cách trình duyệt của bạn giao tiếp với máy chủ web. Trong ví dụ này, https là Hypertext Transfer Protocol Secure. Kết nối https được mã hóa và an toàn hơn http thông thường. Lược đồ được phân tách bằng dấu: và // bắt đầu tên miền.
  • www.movies.com là tên miền hoặc máy chủ lưu trữ. Đây là máy chủ web mà dữ liệu đang được yêu cầu. Domain luôn bao gồm miền cấp cao nhất. Trong trường hợp này, miền cấp cao nhất là .com, các miền cấp cao nhất khác là .gov .net .org .vn, v.v.
  • :443 là port. Port được sử dụng để truy cập tài nguyên trên máy chủ web. Nếu máy chủ web ở trên một port tiêu chuẩn (: 80 cho http và: 443 cho https), bạn sẽ không thấy nó trong URL. Nếu không, nó là bắt buộc và nó sẽ cấp quyền truy cập vào các tài nguyên trên máy chủ web đó.
  • movie/index.html được gọi là đường dẫn đến tập tin. Nó chỉ ra đường dẫn đến tài nguyên cụ thể trên máy chủ web mà client (trình duyệt) đang yêu cầu truy cập.
  • ? type = Horti & imdb = 8 là các tham số (parameters) của URL. Về cơ bản, đây là các cặp giá trị khóa được phân tách bằng ký hiệu &. Vì trong URL cụ thể này có một? bạn có thể tham khảo? type =ror & imdb = 8 làm chuỗi truy vấn. Các máy chủ web đều là duy nhất và xử lý các thông số khác nhau.
  • #Blockbusters là mỏ neo (anchor). Một số có thể gọi đây là mã nhận dạng phân đoạn (fragment identifier). Nó sẽ luôn bắt đầu bằng dấu thăng #. Về cơ bản nó là một tài liệu tham khảo trang nội bộ. Anchor sẽ đưa bạn đến vị trí cụ thể trên một trang web. Nếu đó là một trang web dài có thể nó sẽ đưa bạn đến một phần hoặc một phần cụ thể của trang.

Q12: Ba giai đoạn của sự lan truyền sự kiện (event propagation) là gì?

Propagation (lan truyền) xảy ra với việc capturing trước sau đó bubbling .Trong giai đoạn capturing các sự kiện truyền từ Window qua  DOM tree cho đến khi nó đến nút đích. Ví dụ: nếu bạn nhấp vào một hyperlink, nó có thể đi từ  phần tử <html>, đến  <body>, đến <div>, và đến <p> .

capturing &  bubbling - câu hỏi phỏng vấn JavaScript
Image: Tutorialrepublic

Sau đó, sau khi phần tử mục tiêu được truy cập (trong đoạn code này có thể là event.target), giai đoạn bubbling xảy ra.

Bubbling ngược lại với giai đoạn capturing. Trong giai đoạn bubbling, các sự kiện nổi bong bóng (buble up) lên cây DOM. Vì vậy, nó sẽ đi từ phần tử đích đến tận Window. Trong ví dụ của chúng ta ở trên, sau khi siêu liên kết được nhấp vào, nó sẽ đi từ phần tử <p> có liên kết, đến phần tử <div>, đến phần tử <body>, đến phần tử <html> và cuối cùng là Cửa sổ.

Các câu hỏi phỏng vấn JavaScript có độ khó cao

Q13: Kết quả của đoạn code sau là gì?

0.1 + 0.2 === 0.3

Điều này đáng ngạc nhiên trả về false! Tại sao? Bởi vì dấu chấm động không được biểu diễn đúng cách bên trong. Vì vậy, ví dụ 0,1 + 0,2 sẽ bằng 0,30000000000000004. Bạn có thấy kỳ lạ không? Lý do cho điều này là máy tính không thể biểu diễn float một cách chính xác. Một cách giải quyết sẽ là làm tròn số khi thực hiện các phép toán số học với số thập phân. Hãy cẩn thận, câu hỏi phỏng vấn JavaScript này tưởng đơn giản nhưng có thể khiến bạn mất điểm đấy

Q14: Từ khóa “this” hoạt động như thế nào? Hãy cho một ví dụ?

Từ khóa this của JavaScript là một trong những khái niệm cơ bản nhưng cũng dễ gây nhầm lẫn nhất của ngôn ngữ này, không chỉ khi bạn phải trả lời câu hỏi “this” là gì trong phỏng vấn về JavaScript

Từ khóa this đại diện cho một đối tượng. Cụ thể nó là đối tượng đang thực thi  cose JS hiện tại. Có nghĩa là mọi hàm JS đều có một tham chiếu đến ngữ cảnh thực thi của nó (cách hàm được gọi) được gọi là this.

function state() { console.log(this.name); } var name = "NY"; var obj1 = { name: "IN", state: state }; var obj2 = { name: "CA", state: state }; state(); // "NY" obj1.state(); // "IN" obj2.state(); // "CA"

Công việc của hàm state () là kiểm tra giá trị của this.name. Điều này có nghĩa là nó in giá trị của thuộc tính name của ngữ cảnh thực thi hiện tại (this object).

Q15: Đoạn mã này trả về True hay False?

Number.MIN_VALUE > 0;

Câu trả lời là True! Bạn sẽ nghĩ MIN_Value là 0 nhưng thực tế Number.MIN_VALUE là 5e-234. Đây là số dương nhỏ nhất có thể được biểu diễn dưới dạng số thực. Về cơ bản, nó là giá trị gần nhất mà bạn có thể nhận được là 0. Vậy giá trị nhỏ nhất là bao nhiêu? Number.NEGATIVE_INFINITY.

Q16. Tại sao Math.max() nhỏ hơn Math.min()?

Việc Math.max ()> Math.min () trả về false nghe có vẻ sai, nhưng nó đúng là như vậy.

Nếu không có đối số nào được đưa ra, Math.min () trả về infinity và Math.max () trả về -infinity. Đây chỉ đơn giản là một phần của đặc tả cho các phương thức max () và min (), nhưng nó có logic đằng sau. Để hiểu tại sao, hãy xem đoạn code sau:

Math.min(1) // 1 Math.min(1, infinity) // 1 Math.min(1, -infinity) // -infinity

Nếu -infinity được coi là đối số mặc định của Math.min (), thì mọi kết quả sẽ là -infinity, điều này khá vô dụng! Trong khi đó, nếu đối số mặc định là infinity, thì việc thêm bất kỳ đối số nào khác sẽ trả về số đó và đó là hành vi mà chúng ta muốn.

Q17: Tại sao 018 Trừ 017 lại bằng 3? 

Việc 018 – 017 trả về 3 là kết quả của việc chuyển đổi kiểu. Trong trường hợp này, chúng ta đang nói về số bát phân (octal numbers).

Trong JavaScript, tiền tố 0 sẽ chuyển đổi bất kỳ số nào thành bát phân. Tuy nhiên, 8 không được sử dụng trong hệ bát phân và bất kỳ số nào có chứa 8 sẽ được chuyển đổi âm thầm thành số thập phân thông thường.

Do đó, 018 – 017 trên thực tế tương đương với biểu thức thập phân 18 – 15, vì 017 là bát phân nhưng 018 là thập phân.

Lưu ý: để hiểu hơn về số bát phân để trả lời tốt câu hỏi phỏng vấn JavaScript này bạn nên tìm hiểu thêm về octal numbers tại đây

Các câu hỏi phỏng vấn JavaScript tổng hợp

Q18: Function Expression khác thế nào với Function Declaration?

Một Function declaration (khai báo hàm) sử dụng từ khóa function, theo sau là tên của hàm. Ngược lại, một Function Expression (biểu thức hàm)  bắt đầu bằng var, let hoặc const, theo sau là tên của hàm và toán tử gán =. Dưới đây là một số ví dụ:

// Function Declaration function sum(x, y) { return x + y; }; // Function Expression: ES5 var sum = function(x, y) { return x + y; }; // Function Expression: ES6+ const sum = (x, y) => { return x + y };

Trong cách sử dụng, sự khác biệt chính là Function declaration được thực hiện theo cơ chế hoisting, trong khi Function Expression thì không. Điều đó có nghĩa là các khai báo hàm được trình thông dịch JavaScript chuyển lên đầu phạm vi của chúng và do đó bạn có thể xác định một khai báo hàm và gọi nó ở bất kỳ đâu trong code của bạn. Ngược lại, bạn chỉ có thể gọi một biểu thức hàm theo trình tự tuyến tính: bạn phải xác định nó trước khi gọi nó.

Q19: Sự khác nhau giữa var, let và const là gì?

Ba từ khóa này có các cách tiếp cận khác nhau trong phép gán (assignment), cơ chế hoisting và phạm vi (scope) – vì vậy chúng ta sẽ so sánh theo 3 từ khóa này.

1/ Assigment (phép gán)

Sự khác biệt cơ bản nhất là letvar có thể được gán lại (re-assigned) trong khi const thì không. Điều này làm cho const trở thành lựa chọn tốt nhất cho các biến không cần thay đổi và nó sẽ ngăn ngừa những sai lầm như vô tình gán lại. Lưu ý rằng const cho phép biến thay đổi (variable mutation), có nghĩa là nếu nó đại diện cho một mảng hoặc một đối tượng, nó có thể thay đổi. Bạn chỉ không thể chỉ định lại chính biến đó.

Cả let và var đều có thể được gán lại, nhưng let có những lợi thế đáng kể so với var, khiến nó trở thành lựa chọn tốt hơn trong hầu hết, nếu không phải là tất cả các trường hợp mà một biến cần thay đổi.

2/ Hoisting

Tương tự như sự khác biệt giữa khai báo hàm và biểu thức (câu hỏi 18), các biến được khai báo bằng var luôn được đưa lên đầu phạm vi tương ứng của chúng, trong khi các biến được khai báo bằng const let được cũng được đưa lên, nhưng nếu bạn cố gắng truy cập chúng trước khi chúng đã khai báo, bạn sẽ gặp lỗi ‘vùng chết tạm thời’ (temporal dead zone).  Đây là hành vi hữu ích, vì var có thể dễ bị lỗi hơn, chẳng hạn như tình cờ bị gán lại. Hãy xem ví dụ sau:

var x = "global scope"; function foo() { var x = "functional scope"; console.log(x); } foo(); // "functional scope" console.log(x); // "global scope"

Ở đây, kết quả của foo () và console.log (x) đúng như chúng ta mong đợi. Nhưng điều gì sẽ xảy ra nếu chúng ta bỏ var thứ hai?

var x = "global scope"; function foo() { x = "functional scope"; console.log(x); } foo(); // "functional scope" console.log(x); // "functional scope"

Mặc dù được định nghĩa trong một hàm, x = "function scope" đã ghi đè biến toàn cục. Chúng ta cần lặp lại từ khóa var để chỉ định rằng biến thứ hai x chỉ được xác định phạm vi cho foo ()

3/ Scope

Trong khi var là phạm vi chức năng (function-scoped), let và const là phạm vi khối (block-scoped). Nói chung, một khối là bất kỳ code nào trong dấu ngoặc nhọn {}, bao gồm các hàm, câu lệnh điều kiện và vòng lặp. Để xem sự khác biệt, hãy xem đoạn mã sau:

var a = 0; let b = 0; const c = 0; if (true) { var a = 1; let b = 1; const c = 1; } console.log(a); // 1 console.log(b); // 0 console.log(c); // 0

Trong khối điều kiện trên, biến toàn cục var a đã được định nghĩa lại, nhưng let b và const c trong phạm vi toàn cục thì không. Nói chung, việc đảm bảo các nhiệm vụ cục bộ vẫn là cục bộ sẽ giúp mã sạch hơn và ít lỗi hơn.

Q20: Điều gì xảy ra nếu bạn xác định một biến mà không có từ khóa?

Điều gì sẽ xảy ra nếu bạn xác định một biến mà không sử dụng từ khóa? Về mặt kỹ thuật, nếu x chưa được xác định, thì x = 1 là viết tắt của window.x = 1. và đó là nguyên nhân phổ biến gây ra rò rỉ bộ nhớ.

Để ngăn chặn hoàn toàn việc viết tắt này, bạn có thể sử dụng chế độ strict mode – được giới thiệu trong ES5 – bằng cách sử dụng use strict ở đầu tài liệu hoặc một hàm cụ thể. Khi đó, nếu bạn khai báo một biến mà không có từ khóa, bạn sẽ gặp lỗi: Uncaught SyntaxError: Unexpected indentifier.

Q21: event delegation là gì?

Event delegation được sử dụng để phản hồi các sự kiện (event) của người dùng thông qua một node cha thay vì mỗi node con. Với Event Delegation, bạn có thể liên kết một trình xử lý sự kiện (event handler) với một phần tử parent mà phần tử đó sẽ xử lý bất kỳ sự kiện nào xảy ra trên một trong các phần tử con của nó.

Điều này cũng hữu ích khi chúng ta thêm các  node bổ sung thông qua JavaScript vì theo cách này, chúng ta không cần phải liên kết lại các sự kiện.

<!-- thay vi: --> <ul> <li onclick="console.log(event.type);">One</li> <li onclick="console.log(event.type);">Two</li> <li onclick="console.log(event.type);">Three</li> </ul> <!-- chung ta co the: --> <ul onclick="console.log(event.type);"> <li>One</li> <li>Two</li> <li>Three</li> </ul>

Q22: Hoisting là gì?

Chúng ta đã đê cập đến hoisting ở câu hỏi phỏng vấn JavaScript 19 bên trên. Hoisting có nghĩa là JavaScript sẽ di chuyển các khai báo lên đầu mọi phạm vi (scope). Nó thường là một khía cạnh không xác định của JavaScript, vì vậy để tránh tạo ra lỗi, bạn nên khai báo tất cả các biến ở đầu mọi phạm vi. Nó chỉ đơn giản là bạn có thể sử dụng một biến trước khi nó được khai báo.

Điều quan trọng là chỉ các biến được khai báo với var mới được lưu trữ, các khai báo letconst thì không. Các khởi tạo (Initializations) cũng không được hoisting, chỉ có các khai báo (decleration) là theo cơ chế này.

Hãy xem một ví dụ:

// Initializations are not hoisted, therefore // This will log out undefined console.log(x); var x = 5; // Declarations are hoisted, therefore // This will log out 5 console.log(y); var y; y = 5;

Q23: JavaScript có phải là typed language?

Bản chất JavaScript là weak typed language. Điều này cho phép chúng ta sử dụng chuyển đổi kiểu ngầm định. Hãy xem ví dụ sau:

// This will return "12" as 1 will be converted into a string 1 + "2" typeof (1 + "2") // Will return "string" // This will return 12 as "12" will be converted into a number "12" * 1 typeof ("12" * 1) // Will return "number"

Mặc dù chúng ta không chỉ rõ rằng mình muốn chuyển đổi chuỗi / số sang định dạng khác, nhưng JavaScript đã ngầm thực hiện việc đó.

Q24: Prototype chain trong JavaScript là gì?

Kế thừa (inheritance) trong JavaScript được gọi là kế thừa nguyên mẫu (prototypal inheritance). Khi bạn cố gắng truy cập thuộc tính của một đối tượng, thuộc tính đó sẽ không chỉ được tìm kiếm trong đối tượng mà còn trên nguyên mẫu của đối tượng, trên nguyên mẫu của nguyên mẫu và điều này tiếp tục cho đến khi tìm thấy một thuộc tính phù hợp hoặc chúng ta tiếp cận cuối của chuỗi nguyên mẫu.

Điều tương tự cũng xảy ra đối với các phương pháp. Trong JavaScript, mọi thứ đều được kế thừa từ Object.

prototype chain - câu hỏi phỏng vấn JavaScript
Image: kickoff.tech

Q25: DOM là gì?

DOM (Document Object Model) là một API liên kết các phần tử tài liệu HTML và XML – được gọi là các nút (node) – thành một cấu trúc cây. Nó cũng chứa thông tin về các mối quan hệ con-cha giữa các nút trong tài liệu. Các nút này là các đối tượng có thể được thao tác thông qua JavaScript và mọi thay đổi được thực hiện đối với chúng như kiểu (styles), nội dung (Content) hoặc thay đổi vị trí (placement changes)- đều được phản ánh.

DOM (Document Object Model) - câu hỏi phỏng vấn JavaScript
Image: w3schools.com

Q26: Làm thế nào chúng ta có thể kiểm tra xem đó có phải là một mảng hay không?

Có nhiều cách khác nhau để kiểm tra xem một thứ có thực sự là một mảng array)  hay không:

// Check the constructor variable.constructor === Array // Check if the variable is an instance of Array variable instanceof Array // Check its prototype Object.prototype.toString.call(variable) === '[object Array]'; // Use the built in isArray Array.isArray(obj);

Q27: Làm thế nào chúng ta có thể hợp nhất hai mảng?

Bạn có thể sử dụng cấu trúc lại Array.concat () hoặc ES6 để hợp nhất hai array:

// Using Array.concat const array1 = ['📗', '📘']; const array2 = ['📕', '📙']; array1.concat(array2); // Using ES6 destructuring const array1 = ['📗', '📘']; const array2 = ['📕', '📙']; [...array1, ...array2];

Q28: Làm thế nào để bạn làm trống (empty) một mảng?

Có một số cách để thực hiện việc này, đơn giản nhất là gán lại biến cho một mảng trống. Hãy xem qua các cách:

const array = [1, 2, 3]; // Assigning to an empty array array = []; // Setting its length property to 0 array.length = 0; // Using splice with the array's length array.splice(0, array.length);

Q29: Hãy cho biết những cách khác nhau để tạo các object trong JavaScript là gì?

Các cách khác nhau để tạo ra các object trong JavaScript:

// Using object literals const obj = { ... }; // Using the new keyword function Obj(props) { this.props = props; }; const obj = new Obj('👋'); // Using Object.create() function newObject(props) { this.props = props; }; const obj = Object.create(newObject); // Using Object.assign() const obj = { ... }; const copy = Object.assign({}, obj);

Q30: Làm cách nào để sao chép các đối tượng trong JavaScript?

Nhân bản (clone) đối tượng có thể khó vì chúng ta có thể có nhân bản nông (shallow clone) tức chỉ sao chép đối tượng nhưng giữ tham chiếu,  hoặc nhân bản sâu (deep clone), tức tạo một bản sao mới với một tham chiếu mới.

const obj = { ... }; // Shallow copy can be done by using assignment: const shallow = obj; // Deep copy examples: // Using JSON.parse & JSON.stringify JSON.parse(JSON.stringify(obj)); // Using Object.assign const deep = Object.assign({}, obj); // Using object spread const deep = { ...obj };

Q31: Cho biết sự khác nhau giữa bind, call và apply?

.bind () được sử dụng để gọi một hàm với một ngữ cảnh nhất định. Sử dụng .bind () sẽ không gọi hàm, chỉ sửa đổi ngữ cảnh.

.call ().apply () sẽ gọi hàm ngay lập tức và sửa đổi ngữ cảnh. Sự khác biệt giữa call()apply():

  • .call () chấp nhận một danh sách các giá trị làm đối số của nó
  • .apply () chấp nhận một mảng làm đối số của nó

Sử dụng .bind () khi bạn muốn sửa đổi ngữ cảnh nhưng bạn muốn gọi hàm sau này.

Sử dụng .call () hoặc .apply () khi bạn muốn sửa đổi ngữ cảnh và bạn muốn gọi hàm ngay lập tức.

Q32. AJAX là gì? Những ưu điểm và nhược điểm của nó là gì?

AJAX là viết tắt của Asynchronous JavaScript And XML và nó hoạt động theo cách sau:

Bất cứ khi nào một sự kiện xảy ra trên một trang web, ví dụ: người dùng nhấp vào một button, một yêu cầu XHR được tạo bởi JavaScript sẽ được gửi đến máy chủ. Máy chủ xử lý yêu cầu và trả về phản hồi cho trang web sau đó được xử lý bằng JavaScript và các hành động cần thiết được thực hiện, ví dụ: cập nhật nội dung trang.Ưu điểm của AJAX

  • Cập nhật một trang web mà không thực sự tải lại nó
  • Yêu cầu và nhận dữ liệu từ máy chủ sau khi trang được tải xong
  • Gửi dữ liệu đến máy chủ ở background

Nhược điểm của AJAX:

  • AJAX sẽ không hoạt động trong trường hợp JavaScript bị disabled
  • Có thể làm cho button quay lại của trình duyệt trở nên vô dụng
  • Vì AJAX làm tăng kích thước mã, nó có thể làm cho trang web của bạn dễ bị tấn công bởi các mối đe dọa bảo mật

Q33: Promise có thể có ở trạng thái nào?

Một promise luôn ở một trong ba trạng thái sau:

  • Pending (đang chờ xử lý): đây là trạng thái ban đầu của promise
  • Fulfilled (hoàn thành): promise đã được thực hiện thành công.
  • Rejected (bị từ chối): promise đã bị từ chối, có lỗi.

Một pending promise có thể được thực hiện với một giá trị, hoặc bị từ chối với một lý do.

Q34: Ưu điểm và nhược điểm của việc sử dụng Promises là gì?

Ưu điểm của việc sử dụng Promises trong JavaScript:

  • Chúng có thể kết hợp, không giống như các lệnh gọi lại, do đó chúng tôi có thể tránh các địa chỉ gọi lại
  • Bạn có thể dễ dàng thực thi mã với Promise.all khi nhiều phản hồi được trả về
  • Bạn chỉ có thể đợi một kết quả từ các lời hứa đồng thời đang chờ xử lý với sự trợ giúp của Promise.race
  • Bạn có thể viết mã không đồng bộ một cách đồng bộ nếu bạn sử dụng nó kết hợp với async / await.

Một số nhược điểm của nó là:

  • Chúng chỉ có thể hoạt động trên một giá trị duy nhất tại một thời điểm
  • Chúng không có sẵn trong trình duyệt cũ hơn, chúng phải được làm đầy
  • Chúng chậm hơn so với việc sử dụng lệnh gọi lại, điều này có thể dẫn đến các vấn đề về hiệu suất có thể xảy ra.

Q35: Kể tên một vài built-in methods và giá trị được các built-in method này trả về

Có thể xem một số built-in methods trong bảng sau:

built-in methods trong JavaScript

Q36: Sự khác biệt giữa map () và forEach () là gì?

Cả hai phương thức map()forEach() đều duyệt qua qua các phần tử của một mảng. Sự khác biệt ở đây là map tạo ra một mảng mới trong khi forEach thì không. Nếu bạn tuân theo mô hình lập trình chức năng và bạn muốn giữ cho dữ liệu không thay đổi thì bạn nên sử dụng map. Nếu bạn muốn thay đổi các phần tử của mảng ban đầu, bạn nên sử dụng forEach. Hiểu ngắn gọn:

  • forEach thay đổi các mục gốc trong mảng
  • map trả về một mảng đã biến đổi trong khi vẫn giữ nguyên bản gốc

Q36: Toán tử typeof dùng để làm gì?

Toán tử typeof được sử dụng để lấy kiểu dữ liệu của toán hạng (operand) của nó. Toán hạng có thể là một chữ hoặc một cấu trúc dữ liệu, ví dụ như một biến, một hàm hoặc một đối tượng. Nó là một toán tử một ngôi được đặt trước toán hạng đơn của nó, có thể thuộc bất kỳ kiểu nào. Giá trị của nó là một chuỗi chỉ ra kiểu dữ liệu của toán hạng.

// This will return "string" typeof "1" // This will return "number" typeof 1 // This will return "boolean" typeof true // This will return "undefined" typeof undefined // This will return "symbol" typeof Symbol() // This will return "object" typeof []

Q37: Làm thế nào để bạn kiểm tra xem đó có phải là NaN?

NaN là viết tắt của Not-a-Number. Để kiểm tra xem một cái gì đó có phải là NaN không, chúng ta không thể sử dụng toán tử typeof vì loại NaN là một kiểu dữ liệu numberic (bạn nên tìm hiểu thêm điều này)  . Chúng ta cũng không thể sử dụng value === NaN vì NaN không bằng chính nó. Nhưng vì nó không bằng nhau nên chúng ta có thể kiểm tra giá trị so với chính nó: value! == value. Nếu nó không phải là một số, nó sẽ trả về true. Chúng ta cũng có thể sử dụng hàm Number.isNaN () mới được giới thiệu trong ES6.

// This will give us "number" as a type typeof NaN // We can't do triple equal either since NaN is not equal to itself value === NaN // retun false NaN === NaN // return false // If value is not equal to itself, we can be sure it is NaN value !== value // We can also use the new isNaN Number function introduced in ES6 Number.isNaN(value);

Q38: Sự khác biệt giữa i ++ và ++ i là gì?

Không chỉ là một câu hỏi trong phỏng vấn JavaScript, nhiều lập trình viên vẫn hay lẫn lộn giữa i ++++ i Cả hai đều sẽ tăng giá trị lên 1. Tuy nhiên:

  • i ++, được gọi là bước tăng hậu tố (postfix increment) sẽ trả về giá trị trước khi nó được tăng lên
  • ++ i, được gọi là bước tăng tiền tố (prefix increment) sẽ trả về giá trị sau khi nó được tăng
let i = 0; i++ // Will evaluate to 0 ++i // Will evaluate to 1

Q39: Làm thể nào để kiểm tra một chuỗi ký tự có phải là palindrome không?

Một palindrome là một từ hoặc một cụm từ mà khi ta đọc xuôi hoặc đọc ngược thì nó cũng như nhau. Ví dụ:

isPalindrome('noon'); // true isPalindrome('moon'); // false isPalindrome('level'); // true

Để kiểm tra một chuỗi có phải là palindrome không chúng ta có thể theo cách sau:

// We check if the passed string is equal to the string reversed with split + reverse + join const isPalindrome = (str) => str === str.split('').reverse().join(''); isPalindrome('noon'); // returns true isPalindrome('moon'); // returns false isPalindrome('level'); // returns true

Q40: Hãy cho biết kết quả trả về của đoạn code sau:

for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }

Bạn có thể trả lời sai câu hỏi phỏng vấn JavaScript này nếu cho biết kết quả là 1,2,3. Nó sẽ xuất ra 3, ba lần liên tiếp. Điều này là do setTimeout là một API trình duyệt được chuyển đến cuối của  call stack. Vào thời điểm hàm gọi lại được trả về  call stack, giá trị của i sẽ là 3. 

Q41: Short-circuits là gì trong JavaScript?

Có ba toán tử logic trong JavaScript:

  • AND &&
  • OR ||
  • NOT!

Trong JavaScript, toán tử Logical OR trả về true nếu một trong hai toán hạng là true:

true || true; // true true || false; // true false || false; // false

Hai khía cạnh quan trọng của toán tử logic trong JavaScript là chúng đánh giá từ trái sang phải và chúng có tính đoản mạnh (short-circuiting)

Điều này có nghĩa là khi JavaScript đánh giá một biểu thức, lấy ví dụ biểu thức OR, nếu toán hạng đầu tiên là true, thì JavaScript bị đoản mạch và không xét đến toán hạng thứ hai. Trong ví dụ bên dưới, dấu hoa thị (****) biểu thị bất kỳ giá trị nào – nó đơn giản không quan trọng nó là gì vì JavaScript thậm chí sẽ không bao giờ xét đến nó trong Logical OR.

true || **** // true

Ví dụ thêm về đoản mạnh:

// Short circuit with logical OR // If this.fileName is undefined, 😶 will be used as a fallback value const fileName = this.fileName || '😶'; // Short circuit with logical AND // If hasFireworks is evaluated to true, makeItFestive will be executed hasFireworks && makeItFestive(); // This is equivalent to: if (hasFireworks) { makeItFestive(); }

Nguồn suorce code tại đây. Bạn cũng có thể tham khảo thêm về Short-circuit evaluation

Q42: Hãy giải thích về vòng lặp For-in

Vòng lặp for-in được thiết kế đặc biệt để lặp qua tất cả các thuộc tính của đối tượng theo cách thức từng bước. Nó chọn một thuộc tính từ đối tượng trong mỗi lần lặp và thực hiện các hoạt động cần thiết trên đó. Hãy xem một ví dụ:

var user = { "name": "Juan", "country": "Germany", "website": "livecodestream.dev" }; for(var key in user) { if(user.hasOwnProperty(key)) { console.log(key + " -> " + user[key]); } }

Kết quả:

name -> Juan country -> Germany website -> livecodestream.dev

Xem thêm về câu hỏi này tại đây

Q43: Làm thế nào để xử lý các ngoại lệ (handle exceptions) trong JavaScript?

Nếu một biểu thức đưa ra lỗi, bạn có thể xử lý chúng bằng câu lệnh try… catch.Ý tưởng của việc sử dụng cấu trúc này là thử chạy một biểu thức, chẳng hạn như một hàm có đầu vào và bắt các lỗi có thể xảy ra. Ví dụ:

function weekDay(dayNum) { if (dayNum < 1 || dayNum > 7) { throw 'InvalidDayNumber' } else { return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][dayNum - 1]; } } try { // Try to run the following let day = weekDay(8); console.log(day); } catch (e) { // catch an error if the above try failed let day = 'unknown'; console.log(e); }

Xem nguồn source code

Q44: Web storage là gì?

Web storage là một API cung cấp cách để trình duyệt lưu trữ các cặp khóa-giá trị cho trình duyệt của người dùng cục bộ. Sử dụng Web storage làm cho quá trình này trực quan hơn so với sử dụng cookie. Web storage cung cấp hai cách để lưu trữ dữ liệu:

  • Local storage – lưu trữ dữ liệu cho client mà không có ngày hết hạn.
  • Session storage – chỉ lưu trữ dữ liệu cho một phiên. Dữ liệu sẽ biến mất khi trình duyệt bị đóng.

Dưới đây là một ví dụ về cách bạn có thể lưu, truy cập và xóa một mục khỏi sessionStorage:

// Save data to sessionStorage sessionStorage.setItem('favoriteColor', 'gray'); // Get the color from the sessionStorage let data = sessionStorage.getItem('favoriteColor'); console.log(data); // Remove saved color preset from sessionStorage sessionStorage.removeItem('favoriteColor'); // Remove ALL the saved data from sessionStorage sessionStorage.clear();

Và đây là cách bạn có thể làm điều tương tự bằng cách sử dụng localStorage:

// Save data to localStorage localStorage.setItem('favoriteColor', 'gray'); // Get the color from the localStorage let data = localStorage.getItem('favoriteColor'); console.log(data); // Remove saved color preset from localStorage localStorage.removeItem('favoriteColor'); // Remove ALL the saved data from localStorage localStorage.clear();

Q45: Higher Order Functions là gì trong JavaScript

Bất kỳ hàm nào nhận một hàm khác làm đối số được gọi là hàm bậc cao hơn (Higher Order Functions). JavaScript cung cấp một số hàm bậc cao hữu ích để thao tác dữ liệu một cách dễ dàng. Dưới đây là một số chức năng quan trọng mà bạn nên biết:Phương thức map giúp truyền một hàm để chuyển đổi từng mục trong một mảng.

const numbers = [1, 2, 3]; const doubles = numbers.map(num => num * 2) //[2, 4, 6]

Filter nhận một tập hợp dữ liệu và bạn có thể chuyển một hàm có điều kiện trả về một tập hợp con của tập hợp.

const numbers = [1, 2, 3]; const isGreaterThanOne = numbers.filter(num => num > 1) //[2, 3]

Phương thức rút gọn nhận một hàm có hai đối số (accumulator & item). Chúng ta cũng có thể trả về tổng số của tất cả các mục mảng bằng cách sử dụng phương thức Reduce như chúng ta đã làm trong ví dụ bên dưới.

const numbers = [1, 2, 3]; const mySum = numbers.reduce((accumulator, num) => accumulator + num) // returns: 6.

Q46: Class là gì?

Classes là một khuôn mẫu  để tạo các đối tượng. Chúng được xây dựng trên các nguyên mẫu (prototypes) và chúng cung cấp cú pháp để dễ dàng để viết các hàm khởi tạo cho lập trình hướng đối tượng trong JavaScript. Để tạo một  JavaScript class, bạn sẽ phải sử dụng từ khóa  class và sử dụng các phương thức khởi tạo bên trong nó. Hãy xem ví dụ:

class Car { constructor(name, year) { this.name = name; this.year = year; } }

Bây giờ bạn có thể tạo một đối tượng mới có tên là “myCar” bằng cách sử dụng lớp đó với ví dụ bên dưới:

let myCar = new Car("Ferrari", 2021);

Bạn cũng có thể kế thừa các phương thức và thuộc tính từ các lớp khác. Để tạo một lớp kế thừa, hãy sử dụng từ khóa extends. Hãy xem ví dụ dưới đây, nơi chúng ta sẽ tạo một lớp có tên là “Model” sẽ kế thừa các phương thức từ lớp “Car”.

class Car { constructor(brand) { this.carname = brand; } present() { return 'I have a ' + this.carname; } } class Model extends Car { constructor(brand, mod) { super(brand); this.model = mod; } show() { return this.present() + ', it is a ' + this.model; } } let myCar = new Model("Ford", "Mustang");

Phương thức super () tham chiếu đến lớp cha. Bằng cách gọi nó, chúng ta có quyền truy cập vào tất cả các thuộc tính và phương thức của lớp car (lớp cha).

Q47: Currying là gì trong JavaScript?

Currying có nghĩa là biến đổi một hàm có n đối số, thành n hàm của một hoặc ít đối số. Ví dụ: giả sử bạn có một hàm add () tính tổng hai số:

function add(a, b) { return a + b; }

Bạn có thể gọi hàm này bằng cách:

add(2,3)

Sau đó, hãy bắt đầu hàm:

function add(a) { return function(b) { return a + b; } }

Bây giờ bạn có thể gọi hàm này bằng cách:

add(2)(3)

Currying không thay đổi hành vi của một hàm. Nó chỉ thay đổi cách nó được gọi.

Q48: Phương thức PreventDefault () dùng để làm gì?

preventDefault () dùng để hủy bỏ một phương thức.  Nó ngăn không cho sự kiện thực hiện hành vi mặc định. Ví dụ: bạn có thể ngăn việc gửi biểu mẫu khi nhấp vào nút gửi như ví dụ dưới đây:

document.getElementById("link").addEventListener("click", function(event){ event.preventDefault() });

Q49: BOM là gì trong JavaScript

Browser Object Model (BOM) cho phép JavaScript “nói chuyện với” trình duyệt. Nó bao gồm các đối tượng điều hướng, lịch sử, màn hình, vị trí và tài liệu là con của cửa sổ. Browser Object Model không được chuẩn hóa và có thể thay đổi dựa trên các trình duyệt khác nhau.

BOM trong JavaScript

Q50: Làm cách nào để  kiểm tra xem một chuỗi có chứa một chuỗi con hay không?

Có 3 cách khả thi để kiểm tra xem một chuỗi có chứa một chuỗi con hay không,

  • Sử dụng includes: Phương thức String.prototype.includes được cung cấp bởi ES6 để kiểm tra một chuỗi có chứa một chuỗi con:
var mainString = "hello", subString = "hell"; mainString.includes(subString)
  • Sử dụng indexOf: Trong môi trường ES5 trở lên, bạn có thể sử dụng String.prototype.indexOf để trả về chỉ mục của một chuỗi con. Nếu giá trị chỉ mục không bằng -1 thì có nghĩa là chuỗi con tồn tại trong chuỗi chính.
var mainString = "hello", subString = "hell"; mainString.indexOf(subString) !== -1
  • Sử dụng RegEx: Giải pháp này sử dụng phương pháp kiểm tra Regular expressiony (RegExp.test), cho phép kiểm tra đối với biểu thức chính quy
var mainString = "hello", regex = /hell/; regex.test(mainString)

Các nguồn câu hỏi phỏng vấn JavaScript và tài liệu tham khảo

Ngoài các câu hỏi trong bài viết này, bạn có thể tham khảo rất nhiều nguồn câu hỏi phỏng vấn JavaScript từ các nguồn khác:

Các nguồn câu hỏi:

  • 400++ câu hỏi phỏng vấn JavaScript
  • 123 câu hỏi phỏng vấn JavaScript quan trọng
  • 100 câu hỏi và câu trả lời phỏng vấn JavaScript cho năm 2021
  • JavaTpoint JavaScript Interview Questions
  • Những câu hỏi phỏng vấn JavaScript được vote nhiều nhất trên StackOverflow

Các bài viết trên ITguru:

  • JavaScript Cheatsheet cần cho các buổi phỏng vấn
  • Hướng dẫn chuẩn bị cho phỏng vấn JavaScript
  • Các bước chuẩn bị cho phỏng vấn JavaScript và 5 GitHub Repositories bạn nên biết

Tài liệu tham khảo và nguồn cho bài viết này

  • JavaScript Interview Questions You Need To Know — Part III
  • 9 JavaScript Interview Questions
  • 50 JavaScript Interview Questions
  • 5 JavaScript Interview Questions That You Should Know
  • 100 JavaScript Interview Questions and Answers in 2021

 

 

Bạn có biết?

tham gia cộng đồng ITguru trên Linkedin, Facebook và các kênh mạng xã hội khác có thể giúp bạn nhanh chóng tìm được những chủ đề phát triển nghề nghiệp và cập nhật thông tin về việc làm IT mới nhất Linkedin Page: Facebook Group: cơ hội việc làm IT : ITguru.vn

Bạn đánh giá bài viết thế nào?

Submit Rating

Average rating 5 / 5. Vote count: 2

No votes so far! Be the first to rate this post.

Tags: câu hỏi phỏng vấncâu hỏi phỏng vấn JavaScriptDevelopersphỏng vấn developerphỏng vấn ngành CNTT

Từ khóa » Câu Hỏi ôn Tập Javascript