+19

Ôn lại kiến thức Front End qua một số câu hỏi phỏng vấn (phần 1)

Mở đầu

Nhân dịp năm mới Giáp Thìn 2024, xin chúc các bạn một năm mới an khang, thịnh vượng và tràn đầy hạnh phúc! Như một làn gió mới, năm mới mang đến cho chúng ta cơ hội để bắt đầu lại với những mục tiêu, dự định và hy vọng mới! Hãy cùng nhau điểm lại một số kiến thức Front end thông qua các câu hỏi phỏng vấn mình sưu tầm được nhé!

Lĩnh vực Javascript

Câu hỏi: Debounce và Throttle trong Javascript là gì? Chỉ ra điểm khác nhau và cách thực hiện?

Tham khảo:

Trong JavaScript, Debounce và Throttle là hai kỹ thuật được sử dụng để kiểm soát số lần thực thi hàm trong một khoảng thời gian nhất định. Mục tiêu chính là để tối ưu hóa hiệu suất ứng dụng, đặc biệt trong các trường hợp xử lý sự kiện liên tục như nhập liệu, cuộn trang, hoặc di chuyển chuột. Mặc dù cả hai đều giảm số lần gọi hàm, nhưng chúng hoạt động theo cách khác nhau.

Debounce giúp đảm bảo rằng một hàm chỉ được thực thi sau một khoảng thời gian nhất định trôi qua mà không có bất kỳ lần gọi nào khác. Nếu hàm được gọi lại trước khi khoảng thời gian đó kết thúc, nó sẽ bị hoãn lại cho đến khi không có lần gọi nào khác trong khoảng thời gian đó. Điều này hữu ích trong việc giảm số lần xử lý không cần thiết, ví dụ như khi tìm kiếm tức thì trong khi người dùng đang nhập.

Cách thực hiện:

function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(context, args), wait);
  };
}

Throttle giúp đảm bảo rằng một hàm chỉ được thực thi không quá một lần trong mỗi khoảng thời gian xác định. Khác với debounce, throttle sẽ không hoãn việc thực thi hàm mà là giới hạn tần suất thực hiện. Điều này rất hữu ích trong các trường hợp như cuộn trang hoặc theo dõi vị trí của chuột, khi bạn muốn cập nhật UI một cách mượt mà nhưng không quá thường xuyên.

Cách thực hiện:

function throttle(func, limit) {
  let inThrottle;
  return function() {
    const context = this, args = arguments;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

Điểm khác biệt:

  • Thời gian thực thi: Debounce đảm bảo rằng hàm chỉ thực thi sau một khoảng thời gian không hoạt động; throttle giới hạn số lần thực thi hàm trong một khoảng thời gian cố định.
  • Mục đích sử dụng: Debounce thích hợp cho các hành động cần đợi cho đến khi hoạt động ngừng lại (ví dụ: nhập tìm kiếm); throttle hữu ích khi bạn cần đảm bảo hàm không thực thi quá thường xuyên trong một khoảng thời gian liên tục (ví dụ: cập nhật vị trí cuộn).

Ngoài ra các bạn có thể tham khảo thêm bài viết https://viblo.asia/p/debounce-va-throttle-trong-javascript-6J3ZgOpMZmB để tìm hiểu sâu hơn nhé!

Câu hỏi: Phân biệt setTimeout, Promise, Async/Await?

Tham khảo:

setTimeout, Promise, và async/await là ba cơ chế trong JavaScript được sử dụng để xử lý các hoạt động bất đồng bộ. Mỗi cơ chế có đặc điểm và cách sử dụng riêng, phục vụ cho các mục đích khác nhau trong việc xử lý các tác vụ không đồng bộ.

setTimeout

  • Đặc điểm: setTimeout là một hàm cho phép bạn thiết lập một timer mà sau khi hết thời gian định trước, một hàm callback sẽ được thực thi. setTimeout không dừng việc thực thi của mã nguồn để chờ timer kết thúc mà tiếp tục thực thi các lệnh tiếp theo.
  • Sử dụng: Thích hợp cho việc thiết lập một đoạn mã để chạy sau một khoảng thời gian nhất định, ví dụ: hiển thị thông báo sau một thời gian, hoặc để trì hoãn việc thực thi một hàm.

Promise

  • Đặc điểm: Promise là một đối tượng đại diện cho sự hoàn thành hoặc thất bại của một hoạt động bất đồng bộ nào đó trong tương lai. Một Promise có ba trạng thái: pending (chờ), fulfilled (hoàn thành), và rejected (bị từ chối).
  • Sử dụng: Promise được sử dụng để xử lý các hoạt động bất đồng bộ một cách linh hoạt hơn. Bạn có thể gắn kết các phản ứng với sự thành công hoặc thất bại của hoạt động bất đồng bộ, và xâu chuỗi (chain) nhiều Promise lại với nhau.

Async/Await

  • Đặc điểm: async/await là một cú pháp mới trong JavaScript giúp việc làm việc với các hoạt động bất đồng bộ trở nên dễ dàng và gần giống với cách viết mã đồng bộ. Một hàm được khai báo với từ khóa async có thể chứa biểu thức await, dừng việc thực thi hàm đó cho đến khi Promise được await hoàn thành.
  • Sử dụng: Dùng để viết mã bất đồng bộ một cách rõ ràng và dễ đọc hơn, giảm thiểu sự phức tạp khi xử lý các Promise và các hoạt động bất đồng bộ khác. async/await giúp quản lý các chuỗi Promise một cách hiệu quả và trực quan.

Phân biệt

  • Thực thi bất đồng bộ: setTimeout được sử dụng cho việc đơn giản là trì hoãn thực thi; Promise cung cấp một cơ chế quản lý kết quả của các hoạt động bất đồng bộ; trong khi async/await giúp việc viết và quản lý mã bất đồng bộ trở nên dễ dàng và gần giống với cách viết mã đồng bộ.
  • Cách tiếp cận: setTimeout là một hàm cơ bản cho việc lập trình bất đồng bộ; Promise là một cấu trúc cao cấp hơn cho việc xử lý các tác vụ bất đồng bộ và có khả năng chuỗi các thao tác; async/await là cú pháp sugar giúp làm việc với Promise trở nên dễ dàng và rõ ràng hơn.

Tham khảo thêm: https://viblo.asia/p/callback-promise-asyncawait-trong-javascript-WAyK86QolxX

Câu hỏi: ['1','2','3'].map(parseInt) trả về gì, vì sao?

Tham khảo:

Khi thực hiện ['1', '2', '3'].map(parseInt), kết quả của biểu thức này sẽ là [1, NaN, NaN]. Để hiểu lý do tại sao lại như vậy, ta cần phải hiểu cách mapparseInt hoạt động.

Cách hoạt động của map: Hàm map gọi một hàm callback với ba tham số: giá trị của phần tử, chỉ số của phần tử, và mảng đang được duyệt. Khi bạn thực hiện map với parseInt như một hàm callback, bạn thực sự đang gọi parseInt với hai tham số: giá trị của phần tử và chỉ số của phần tử đó trong mảng.

Cách hoạt động của parseInt: Hàm parseInt(string, radix) chuyển đổi một chuỗi thành một số nguyên dựa trên cơ số (radix) được chỉ định. Nếu cơ số không được chỉ định hoặc là undefined, cơ số mặc định sẽ là 10, trừ khi chuỗi bắt đầu với các ký tự 0x hoặc 0X, khi đó cơ số sẽ là 16 (hệ thập lục phân). Tuy nhiên, nếu cơ số là 0 hoặc không phải là một số hợp lệ, cơ số cũng sẽ được coi là 10.

Tại sao kết quả lại là [1, NaN, NaN]?

  • Khi parseInt được gọi với '1' làm giá trị và 0 làm chỉ số (radix), cơ số không được chỉ định rõ ràng, do đó parseInt('1', 0) sẽ giả định cơ số là 10 và trả về 1.
  • Khi parseInt được gọi với '2' và chỉ số 1, cơ số được hiểu là 1. Tuy nhiên, cơ số 1 không phải là một giá trị hợp lệ (cơ số hợp lệ từ 2 đến 36), nên parseInt('2', 1) không thể phân tích và trả về NaN.
  • Tương tự, parseInt('3', 2) cũng trả về NaN'3' không phải là một số hợp lệ trong hệ cơ số 2.

Do đó, khi sử dụng parseInt với map như vậy, bạn cần phải cẩn thận về cách parseInt được áp dụng, có thể nhận được các tham số không mong muốn từ map, dẫn đến kết quả không như dự đoán.

Tham khảo thêm: https://viblo.asia/p/til-tai-sao-1-7-11mapparseint-lai-bang-1-nan-3-trong-javascript-Az45bzrQ5xY để tìm hiểu sâu hơn nhé!

Câu hỏi: Giải thích nguyên nhân dẫn tới fetch gửi 2 request (trong đó request đầu trả về status code 204)?

Tham khảo:

Việc fetch gửi hai yêu cầu và yêu cầu đầu tiên trả về 204 là một phần của quy trình kiểm tra và tuân thủ chính sách CORS, đảm bảo rằng các yêu cầu từ trang web này sang trang web khác được thực hiện một cách an toàn và được phép bởi server.

Preflight Request: là một yêu cầu HTTP được gửi bằng phương thức OPTIONS trước yêu cầu thực sự (actual request), để kiểm tra xem trình duyệt có được phép thực hiện yêu cầu đến server không. Điều này thường xảy ra khi yêu cầu thực sự có những đặc điểm nhất định mà CORS policy yêu cầu kiểm tra trước, như:

  • Sử dụng phương thức HTTP khác ngoài GET, HEAD, hoặc POST.
  • POST với Content-Type khác application/x-www-form-urlencoded, multipart/form-data, hoặc text/plain.
  • Yêu cầu bao gồm custom headers.

Lý do gửi 2 yêu cầu:

  • Yêu cầu đầu tiên (Preflight Request): Được gửi tự động bởi trình duyệt sử dụng phương thức OPTIONS, để xác định liệu yêu cầu thực sự có được phép hay không. Server sẽ kiểm tra thông tin CORS trong yêu cầu này, bao gồm Origin, Access-Control-Request-Method, và Access-Control-Request-Headers trong headers của yêu cầu preflight. Nếu server chấp nhận yêu cầu, nó sẽ trả về một phản hồi với mã trạng thái 204 (No Content) hoặc một mã trạng thái khác nhưng kèm theo các headers phù hợp cho phép CORS.
  • Yêu cầu thứ hai (Actual Request): Sau khi nhận được phản hồi chấp nhận từ yêu cầu preflight, trình duyệt sẽ tiếp tục gửi yêu cầu thực sự đến server. Yêu cầu này là yêu cầu chính thức mà bạn muốn thực hiện, ví dụ POST, GET với các headers và body data cụ thể. Nếu yêu cầu này được server xử lý thành công, server sẽ trả về dữ liệu yêu cầu hoặc mã trạng thái thành công tương ứng.

Lĩnh vực HTTP/HTTPs, trình duyệt

Câu hỏi: Trong quá trình bắt tay HTTPs, trình duyệt (client) xác minh tính hợp lệ của chứng chỉ SSL/TLS bằng cách nào?

Tham khảo:

Trong quá trình bắt tay HTTPs, trình duyệt (client) xác minh tính hợp lệ của chứng chỉ SSL/TLS từ server bằng các bước sau:

  1. Kiểm tra thời hạn của chứng chỉ: Trình duyệt kiểm tra xem chứng chỉ còn hợp lệ (chưa hết hạn) hay không.

  2. Xác minh chữ ký của chứng chỉ: Trình duyệt sử dụng Public key của Cơ Quan Chứng Nhận (CA - Certificate Authority) để xác minh chữ ký số trên chứng chỉ. Điều này đảm bảo rằng chứng chỉ không bị giả mạo hoặc thay đổi bởi người không có quyền.

  3. Kiểm tra Chain of Trust: Trình duyệt kiểm tra xem chứng chỉ được cấp bởi một CA tin cậy (đã được cài đặt sẵn trong trình duyệt hoặc hệ điều hành) hay không. Chứng chỉ phải tạo thành một chuỗi tin cậy từ CA gốc đến chứng chỉ được cấp.

  4. So sánh tên miền: Trình duyệt so sánh tên miền mà người dùng đang truy cập với tên miền được liệt kê trong chứng chỉ (thông qua trường Common Name hoặc Subject Alternative Name). Điều này đảm bảo rằng chứng chỉ đang được sử dụng bởi trang web đúng.

  5. Kiểm tra thu hồi chứng chỉ: Trình duyệt có thể kiểm tra xem chứng chỉ đã bị thu hồi hay chưa thông qua các dịch vụ như Certificate Revocation List (CRL) hoặc Online Certificate Status Protocol (OCSP).

Nếu chứng chỉ qua các bước kiểm tra này thành công, trình duyệt sẽ coi kết nối là an toàn và tiếp tục quá trình bắt tay để thiết lập kết nối mã hóa. Ngược lại, nếu có bất kỳ vấn đề nào với chứng chỉ, trình duyệt sẽ cảnh báo người dùng về sự không an toàn của kết nối.

Tham khảo thêm tại https://viblo.asia/p/https-and-tlsssl-WAyK8dDnKxX

Câu hỏi: Việc sử dụng mỗi thẻ img, iframe, script để gửi yêu cầu cross-domain có ưu và nhược điểm gì?

Tham khảo:

Thẻ img

  • Ưu điểm:

    • Đơn giản để sử dụng: Chỉ cần thiết lập thuộc tính src đến URL mong muốn.
    • Tương thích rộng rãi: Hỗ trợ trên tất cả các trình duyệt.
    • Không bị giới hạn bởi chính sách Same-Origin: Có thể tải hình ảnh từ các nguồn cross-domain.
  • Nhược điểm:

    • Chỉ hỗ trợ phương thức GET: Không thể sử dụng cho các yêu cầu POST, PUT, DELETE, etc.
    • Không thể truy cập dữ liệu phản hồi: Không thể xử lý hoặc truy cập dữ liệu trả về từ server.
    • Không thể xử lý lỗi: Không có cách nào để biết liệu yêu cầu có thành công hay không.

Thẻ iframe

  • Ưu điểm:

    • Tích hợp nội dung từ nguồn khác: Có thể hiển thị trang web hoặc nội dung từ domain khác.
    • Hỗ trợ các loại nội dung phức tạp: Không chỉ giới hạn ở hình ảnh mà còn có thể chứa các loại nội dung khác như HTML, CSS, JavaScript.
  • Nhược điểm:

    • Hạn chế trong giao tiếp: Khó khăn trong việc giao tiếp giữa trang chính và trang được nhúng qua iframe nếu không sử dụng các kỹ thuật như window.postMessage.
    • Vấn đề về bảo mật: Có thể bị sử dụng để thực hiện các cuộc tấn công clickjacking nếu không được cấu hình cẩn thận.
    • Ảnh hưởng đến hiệu suất trang web: Việc tải nội dung từ iframe có thể làm chậm tốc độ tải trang.

Thẻ script

  • Ưu điểm:

    • Cho phép thực thi mã JavaScript từ domain khác: Hỗ trợ tải và thực thi script, là cơ sở cho JSONP (JSON with Padding), cho phép truy vấn dữ liệu cross-domain.
    • Tương thích rộng rãi: Hỗ trợ trên tất cả trình duyệt.
    • Khả năng tương tác cao: Có thể nhận dữ liệu phản hồi dưới dạng mã JavaScript và xử lý ngay lập tức.
  • Nhược điểm:

    • Bảo mật: JSONP (thường sử dụng với thẻ script cho cross-domain) không hỗ trợ xác thực và có thể dễ dàng bị tấn công XSS nếu không cẩn thận.
    • Chỉ hỗ trợ phương thức GET: Tương tự như img, không thể sử dụng cho các yêu cầu với phương thức khác như POST.
    • Khó khăn trong việc xử lý lỗi: JSONP không cung cấp cách dễ dàng để xử lý lỗi khi yêu cầu thất bại.

Câu hỏi: Phân biệt HTTP và HTTPs?

Tham khảo:

Easy question, các bạn tìm hiểu qua Google nha!

Câu hỏi: Phân biệt Cookie, sessionStorage, localStorage?

Tham khảo:

Cookie

  • Mục đích: Ban đầu được thiết kế để lưu trữ dữ liệu phiên làm việc (session), nhưng cũng thường được sử dụng để theo dõi hành vi người dùng trên các trang web.
  • Lưu trữ: Dữ liệu được lưu trữ trên cả máy khách và máy chủ. Server có thể đọc và ghi cookie thông qua HTTP headers.
  • Kích thước: Giới hạn kích thước khoảng 4KB cho mỗi cookie.
  • Hết hạn: Có thể thiết lập thời gian hết hạn; sau thời gian này, cookie sẽ tự động bị xóa.
  • Bảo mật: Dễ bị lợi dụng cho các mục đích theo dõi và tấn công do thông tin có thể được truyền qua mỗi yêu cầu HTTP.

sessionStorage

  • Mục đích: Lưu trữ dữ liệu tạm thời trong suốt thời gian duyệt web trong một tab cụ thể.
  • Lưu trữ: Chỉ lưu trữ dữ liệu trên phía máy khách và không gửi dữ liệu đến server trong mỗi yêu cầu HTTP.
  • Kích thước: Có thể lưu trữ khoảng 5MB đến 10MB dữ liệu.
  • Hết hạn: Dữ liệu được lưu trữ trong sessionStorage chỉ tồn tại cho đến khi tab hoặc cửa sổ trình duyệt được đóng lại.
  • Bảo mật: An toàn hơn cookie vì dữ liệu không được gửi đi mỗi lần truy vấn server.

localStorage

  • Mục đích: Lưu trữ dữ liệu không có thời gian hết hạn trên trình duyệt của người dùng.
  • Lưu trữ: Dữ liệu được lưu trữ chỉ trên phía máy khách và không được gửi đến server với mỗi yêu cầu HTTP.
  • Kích thước: Giống như sessionStorage, localStorage cũng cho phép lưu trữ khoảng 5MB đến 10MB.
  • Hết hạn: Dữ liệu lưu trữ không hết hạn và chỉ được xóa qua hành động của người dùng hoặc thông qua mã script.
  • Bảo mật: Tương tự như sessionStorage, localStorage cung cấp bảo mật tốt hơn so với cookie vì dữ liệu không được gửi đi mỗi lần truy vấn server.

Bạn đọc có thể tham khảo thêm tại bài viết https://viblo.asia/p/su-khac-biet-giua-localstorage-sessionstorage-va-cookie-m68Z0Q7jlkG

Câu hỏi: Có thể ngăn chặn tấn công XSS cho cookie bằng các flag nào?

Tham khảo:

HttpOnly Flag

  • Cách hoạt động: Khi một cookie được đánh dấu với cờ HttpOnly, cookie đó không thể được truy cập thông qua các script client-side như JavaScript. Điều này có nghĩa là ngay cả khi một kịch bản độc hại được thực thi thông qua tấn công XSS, nó không thể đọc hoặc đánh cắp cookie có cờ HttpOnly.
  • Mục đích: Giảm thiểu hậu quả của tấn công XSS bằng cách ngăn chặn việc truy cập trái phép vào cookie, đặc biệt là cookie chứa thông tin nhạy cảm như token xác thực.

Secure Flag

  • Cách hoạt động: Cờ Secure đảm bảo rằng cookie chỉ được gửi qua các kết nối HTTPS an toàn, không qua HTTP. Mặc dù không trực tiếp ngăn chặn XSS, việc sử dụng cờ Secure giúp bảo vệ dữ liệu cookie khỏi bị tiết lộ qua các kết nối không mã hóa, giảm rủi ro bị đánh cắp khi dữ liệu được truyền đi.
  • Mục đích: Tăng cường bảo mật cho dữ liệu cookie khi truyền qua mạng, gián tiếp giảm thiểu rủi ro từ các tấn công lợi dụng XSS.

SameSite Flag

  • Cách hoạt động: Cờ SameSite giới hạn trình duyệt gửi cookie trong các yêu cầu cross-site. Có hai giá trị cho cờ này: StrictLax, giúp ngăn chặn việc gửi cookie trong các yêu cầu từ trang web ngoài, điều này giúp giảm rủi ro của các tấn công CSRF (Cross-Site Request Forgery) có thể được sử dụng kết hợp với XSS.
  • Mục đích: Giảm thiểu khả năng tấn công CSRF, một loại tấn công có thể được tăng cường bởi XSS, qua đó giảm thiểu tác động của XSS.

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí