Câu hỏi phỏng vấn JavaScript nhiều cấp độ 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

Tóm Tắt

Các câu hỏi ở Lever 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 được cho phép ép kiểu tài 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 thuầ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 1 số ít tác vụ đã triển khai xong. 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 tin. Thông báo tiên phong “ Starting my JS blog. ” và thông tin 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 tài 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ó những phương pháp ( method ) của riêng nó. Tất cả những kiểu nguyên thủy là không bao giờ thay đổi. 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ử và và để 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ử và và ( đọc là logic AND ) tìm giá trị ( falsy ) tiên phong 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ị sau cuối trong biểu thức. Vì vậy, dòng tiên phong :

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

trả về false vì false là giá trị falsy tiên phong 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ị ở đầu cuối 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à falsy
Ví 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ồn chồn .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ự độc lạ giữa undefined và null là gì ?

Đây là một khái niệm hoàn toàn 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 hoàn toà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 sống sót. Null được gán cho một biến như thể một đại diện thay mặt 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 và 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ử , đến  , đến

, và đến .capturing &  bubbling - câu hỏi phỏng vấn JavaScript

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ử

có liên kết, đến phần tử

, đến phần tử , đến phần tử 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 kinh ngạc trả về false ! Tại sao ? Bởi vì dấu chấm động không được trình 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 hề trình diễn float một cách đúng mực. Một cách xử lý sẽ là làm tròn số khi thực thi những phép toán số học với số thập phân. Hãy cẩn trọng, câu hỏi phỏng vấn JavaScript này tưởng đơn thuần nhưng hoàn toàn có thể khiến bạn mất điểm đấy

Q14 : Từ khóa “ this ” hoạt động giải trí 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_VALUE5e-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 ()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à hiệu quả của việc quy đổi kiểu. Trong trường hợp này, tất cả chúng ta đang nói về số bát phân ( octal numbers ) .Trong JavaScript, tiền tố 0 sẽ quy đổ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 quy đổi bí mật thành số thập phân thường thì .Do đó, 018 – 017 trên trong thực tiễn tương tự 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, letconst là gì?

Ba từ khóa này có những cách tiếp cận khác nhau trong phép gán ( assignment ), chính sách hoisting và khoanh vùng phạm vi ( scope ) – vì thế tất cả 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ả letvar đề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 ()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à khoanh vùng phạm vi tính năng ( function-scoped ), let và const là khoanh vùng 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 { }, gồm có những hàm, câu lệnh điều kiện kèm theo và vòng lặp. Để xem sự độc lạ, 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 bconst 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 lập 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 lập, thì x = 1 là viết tắt của window. x = 1. và đó là nguyên do thông dụng 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 những sự kiện ( sự kiện ) của người dùng trải qua một node cha thay vì mỗi node con. Với Event Delegation, bạn hoàn toàn có thể link một trình xử lý sự kiện ( sự kiện handler ) với một thành phần parent mà thành phần đó sẽ giải quyết và xử lý bất kể sự kiện nào xảy ra trên một trong những thành phần con của nó .Điều này cũng hữu dụng khi tất cả chúng ta thêm những node bổ trợ trải qua JavaScript vì theo cách này, tất cả chúng ta không cần phải link lại những sự kiện .


  • One
  • Two
  • Three
  • One
  • Two
  • Three

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ẽ chuyển dời những khai báo lên đầu mọi khoanh vùng phạm vi ( scope ). Nó thường là một góc nhìn không xác lập của JavaScript, vì thế để tránh tạo ra lỗi, bạn nên khai báo toàn bộ những biến ở đầu mọi khoanh vùng phạm vi. Nó chỉ đơn thuần là bạn hoàn toà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 let const 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 được cho phép tất cả chúng ta sử dụng quy đổ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ù tất cả chúng ta không chỉ rõ rằng mình muốn quy đổi chuỗi / số sang định dạng khác, nhưng JavaScript đã ngầm triển khai việc đó .

Q24 : Prototype chain trong JavaScript là gì ?

Kế thừa ( inheritance ) trong JavaScript được gọi là thừa kế nguyên mẫu ( prototypal inheritance ). Khi bạn nỗ lực truy vấn thuộc tính của một đối tượng người tiêu dùng, thuộc tính đó sẽ không chỉ được tìm kiếm trong đối tượng người dùng mà còn trên nguyên mẫu của đối tượng người dùng, trên nguyên mẫu của nguyên mẫu và điều này liên tục cho đến khi tìm thấy một thuộc tính tương thích hoặc tất 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

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

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ó 1 số ít cách để triển khai việc này, đơn thuần nhất là gán lại biến cho một mảng trống. Hãy xem qua những 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, callapply?

.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ố điểm yếu ké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 1 số ít built-in methods trong bảng sau :

built-in methods trong JavaScript

Q36: Sự khác biệt giữa map ()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 tất cả chúng ta hoàn toàn 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 góc nhìn quan trọng của toán tử logic trong JavaScript là chúng nhìn nhận 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 lý giải về vòng lặp For-in

Vòng lặp for-in được phong cách thiết kế đặc biệt quan trọng để lặp qua toàn bộ những thuộc tính của đối tượng người dùng theo phương pháp từng bước. Nó chọn một thuộc tính từ đối tượng người tiêu dùng trong mỗi lần lặp và thực thi những hoạt động giải trí thiết yếu 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 phân phối cách để trình duyệt tàng trữ những 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 quy trình này trực quan hơn so với sử dụng cookie. Web storage cung ứng hai cách để tàng trữ tài 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 và item ). Chúng ta cũng hoàn toàn có thể trả về tổng số của toàn bộ những mục mảng bằng cách sử dụng phương pháp Reduce như tất cả 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 hoàn toàn có thể tạo một đối tượng người dù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 hoàn toàn có thể gọi hàm này bằng cách :

add(2,3)

Sau đó, hãy mở màn hàm :

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

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

add(2)(3)

Currying không biến hóa hành vi của một hàm. Nó chỉ biến hóa 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 ) được cho phép JavaScript “ trò chuyện với ” trình duyệt. Nó gồm có những đối tượng người dùng điều hướng, lịch sử dân tộc, màn hình hiển thị, vị trí và tài liệu là con của hành lang cửa số. Browser Object Model không được chuẩn hóa và hoàn toàn có thể biến hóa dựa trên những 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 tìm hiểu thêm

Ngoài những câu hỏi trong bài viết này, bạn hoàn toàn có thể tìm hiểu thêm rất nhiều nguồn câu hỏi phỏng vấn JavaScript từ những nguồn khác :

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

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

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

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: https://bit.ly/LinkedinITguru
Facebook Group: https://bit.ly/ITguruvn
cơ hội việc làm IT : ITguru.vn

Bạn nhìn nhận bài viết thế nào ?

Average rating 5 / 5. Vote count : 1No votes so far ! Be the first to rate this post .