0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Môi trường nào không ném lỗi với addEventListener?

Đăng vào 6 tháng trước

• 4 phút đọc

Chủ đề:

KungFuTech

Môi trường không ném lỗi với addEventListener

Giới thiệu

Trong lập trình JavaScript, việc xác định môi trường thực thi của mã có thể dẫn đến những điều thú vị và bất ngờ. Một trong những câu đố thú vị đó là tìm ra môi trường mà trong đó đoạn mã sau không ném lỗi:

javascript Copy
let w = globalThis;
if (!('addEventListener' in globalThis)) throw Error();
while (w) {
    if (Object.hasOwn(w, 'addEventListener')) throw Error();
    w = Object.getPrototypeOf(w);
}
alert('where are we?');

Câu đố này không chỉ thú vị mà còn mở ra những hiểu biết sâu sắc về cách mà JavaScript hoạt động trong các môi trường khác nhau, đặc biệt là trong các tiện ích mở rộng của trình duyệt.

Nội dung của đoạn mã

Đoạn mã trên đầu tiên kiểm tra xem addEventListener có tồn tại trong globalThis hay không. Tuy nhiên, in có nghĩa là thuộc tính riêng hoặc bất kỳ đâu trong prototype. Do đó, chúng ta sẽ tìm kiếm trên prototype bằng cách lặp qua cho đến khi không còn prototype nào có giá trị truthy. Nếu tìm thấy, đoạn mã sẽ ném lỗi.

Cuối cùng, đoạn mã sử dụng alert để hiển thị thông báo. Điều này chứng tỏ rằng đoạn mã vẫn hoạt động, nhưng chúng ta cần phải xác định môi trường mà nó không ném lỗi.

Môi trường nào?

Khi bạn chạy đoạn mã sau:

javascript Copy
let w = globalThis;
while (w) {
  if (Object.hasOwn(w, 'addEventListener')) {
    console.log(`${w} has addEventListener`);
  } else {
    console.log(`${w} does NOT have addEventListener`);
  }
  w = Object.getPrototypeOf(w);
}

Kết quả đầu ra thường sẽ là:

Copy
[object Window] does NOT have addEventListener
[object Window] does NOT have addEventListener
[object WindowProperties] does NOT have addEventListener
[object EventTarget] has addEventListener
[object Object] does NOT have addEventListener

Tuy nhiên, có một nơi kỳ diệu trong trình duyệt mà điều này không nhất thiết phải đúng.

Khi bạn tạo một tiện ích mở rộng, bạn có thể định nghĩa một contentscript cho phép bạn tương tác với document của một trang web mở trong trình duyệt. Mặc dù vậy, nó được thiết kế để được cách ly khỏi không gian của trang web, vì vậy bạn có một ngữ cảnh toàn cầu riêng.

Cách các trình duyệt thực hiện cách ly

Các trình duyệt đã áp dụng những cách tiếp cận rất khác nhau để triển khai sự cách ly này. Cách triển khai của Firefox đang thay đổi chuỗi prototype của đối tượng toàn cầu và giới hạn nó chỉ ở window và prototype của nó, với không gian khác không hiển thị qua mã, nhưng với việc tìm kiếm trường vẫn có thể xuống chuỗi prototype xa hơn.

Chạy vòng lặp while tương tự, kết quả thu được sẽ là:

Copy
[object Window] does NOT have addEventListener
[object Window] does NOT have addEventListener

Tuy nhiên,

Copy
('addEventListener' in window) === true;
typeof window.addEventListener === 'function';

Điều này có vẻ mâu thuẫn, nhưng nếu tôi thay thế globalThis bằng window, tôi nhận được một danh sách các prototype khác:

Copy
[object Window] does NOT have addEventListener
[object Window] does NOT have addEventListener
[object EventTarget] does NOT have addEventListener
[object EventTarget] has addEventListener
[object Object] does NOT have addEventListener

Phân tích và thảo luận

Mặc dù tôi có thể hiểu việc cắt một phần prototype như một phương tiện để cách ly, nhưng nếu nó vẫn ở window, điều đó khó giải thích hơn. Có vẻ như có một số điều kỳ lạ đang xảy ra ở đây mà tôi chưa thể lý giải được.

Thực tiễn tốt nhất

  • Nắm vững cách thức hoạt động của JavaScript trong các môi trường khác nhau: Để tránh nhầm lẫn, hãy luôn kiểm tra môi trường mà mã của bạn đang chạy.
  • Sử dụng globalThis khi cần: Điều này giúp mã của bạn linh hoạt hơn khi làm việc với các môi trường khác nhau.

Cạm bẫy thường gặp

  • Không hiểu rõ cách thức hoạt động của prototype: Điều này có thể dẫn đến việc mã không hoạt động như mong đợi trong các môi trường khác nhau.
  • Bỏ qua sự khác biệt giữa các trình duyệt: Hãy luôn kiểm tra mã của bạn trên nhiều trình duyệt khác nhau để đảm bảo tính tương thích.

Mẹo hiệu suất

  • Giảm thiểu việc truy cập vào prototype: Sử dụng các phương pháp khác để kiểm tra sự tồn tại của thuộc tính thay vì lặp qua prototype nếu không cần thiết.
  • Tối ưu hóa mã: Đảm bảo mã của bạn không chỉ hoạt động mà còn chạy nhanh và hiệu quả.

Giải quyết sự cố

Nếu bạn gặp sự cố khi mã không hoạt động như mong đợi, hãy thử:

  • Kiểm tra các biến toàn cầu trong môi trường của bạn.
  • Đảm bảo rằng tất cả các thuộc tính mà bạn đang cố gắng truy cập đều tồn tại.

Kết luận

Tìm hiểu môi trường mà đoạn mã JavaScript của bạn đang chạy là rất quan trọng. Việc nắm rõ các khái niệm về prototype và cách hoạt động của các trình duyệt có thể giúp bạn viết mã hiệu quả hơn và tránh được những lỗi không mong muốn. Nếu bạn có bất kỳ câu hỏi nào hoặc muốn chia sẻ ý kiến của mình, hãy để lại bình luận bên dưới!

Câu hỏi thường gặp

1. Tại sao addEventListener lại không tồn tại trong một số môi trường?

  • Điều này phụ thuộc vào cách mà môi trường đó triển khai các đối tượng JavaScript và cách thức cách ly giữa các ngữ cảnh.

2. Làm thế nào để kiểm tra sự tồn tại của một thuộc tính?

  • Bạn có thể sử dụng in hoặc Object.hasOwn để kiểm tra sự tồn tại của thuộc tính trong đối tượng.

3. Có cách nào khác để kiểm tra môi trường không?

  • Bạn có thể sử dụng các thư viện như detect-browser để xác định môi trường thực thi của mã.
Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào