Polyfill: định nghĩa, nguyên lý hoạt động và thư viện để mô phỏng API

Tác giả: IT Sectr Đã đăng: 2026-05-19 Thời gian đọc: 9 phút

Polyfill là mã nguồn mô phỏng chức năng bị thiếu (API, phương thức, đối tượng) trong các môi trường không được triển khai nguyên bản. Polyfill cho phép sử dụng các tính năng JavaScript, CSS hoặc Web API hiện đại trong các trình duyệt cũ và môi trường thực thi. Theo MDN Web Docs, polyfill là công cụ chính để cải tiến dần dần và đảm bảo tương thích đa trình duyệt.

Những điểm chính

  • Polyfill là sự mô phỏng phần mềm của một API bị thiếu trong môi trường thực thi nơi API đó chưa được triển khai
  • core-js là thư viện polyfill tiêu chuẩn cho JavaScript hiện đại với hỗ trợ tất cả các đề xuất stage-4
  • Polyfill.io là dịch vụ cung cấp polyfill động chỉ cho trình duyệt của người dùng
  • Transpilation vs polyfill: transpilation chuyển đổi cú pháp (arrow function → function), polyfill thêm phương thức mới (Array.includes, Promise)
  • Phát hiện tính năng (Feature detection) kiểm tra sự tồn tại của triển khai gốc trước khi tải polyfill để tránh xung đột

Polyfill là gì?

Polyfill là một đoạn mã (thường là JavaScript) triển khai chức năng mà môi trường thực thi không hỗ trợ nguyên bản. Thuật ngữ này được Remy Sharp đặt ra vào năm 2009 như một cách chơi chữ: Polyfill tương tự như Polyfilla, một loại bột trét lấp đầy các vết nứt trên tường. Polyfill lấp đầy khoảng trống giữa tiêu chuẩn và hỗ trợ của nó trong một trình duyệt hoặc môi trường thực thi cụ thể.

Polyfill không sửa đổi mã hiện có — nó mở rộng môi trường thực thi. Nếu trình duyệt không hỗ trợ Array.prototype.includes, polyfill sẽ thêm phương thức này vào nguyên mẫu Array trước khi mã chính được thực thi. Polyfill có thể mô phỏng các đối tượng toàn cục mới (Promise, Map, Set, Symbol), phương thức tĩnh (Array.from, Object.assign) và phương thức nguyên mẫu.

Phát hiện tính năng (Feature detection) là cơ chế bắt buộc trước khi cài đặt polyfill. Thay vì kiểm tra user-agent (trình duyệt nào), cần kiểm tra sự tồn tại của phương thức: if (!Array.prototype.includes) { Array.prototype.includes = ... }. Điều này đảm bảo polyfill không ghi đè lên triển khai gốc nếu nó đã tồn tại. Google Analytics và các dịch vụ khác thu thập dữ liệu hỗ trợ API để phân tích.

Khi nào polyfill xuất hiện

Polyfill đầu tiên xuất hiện trong kỷ nguyên Internet Explorer 6–8 (2005–2009), khi các nhà phát triển phát hiện khoảng cách giữa tiêu chuẩn W3C và triển khai trình duyệt. Thuật ngữ này được Remy Sharp giới thiệu vào năm 2009 tại BarCamp London. Polyfill đại chúng đầu tiên là html5shiv (2009) — một thư viện thêm hỗ trợ cho các thẻ HTML5 (<section>, <article>, <nav>) trong Internet Explorer.

Với sự ra đời của ES6 (2015) và chu kỳ cập nhật ECMAScript hàng năm, số lượng polyfill cần thiết đã tăng lên. Mỗi năm, tiêu chuẩn bổ sung các phương thức mới (Array.includes, String.padStart, Object.fromEntries, Promise.allSettled) mà các trình duyệt cũ không hỗ trợ. core-js, bắt đầu như es6-shim vào năm 2014, đã trở thành một giải pháp phổ quát. Tính đến năm 2026, core-js chứa hơn 5.000 mô-đun polyfill cho ES5–ES2025.

Những gì có thể và không thể polyfill

Danh mụcCó thể polyfillKhông thể polyfill
Phương thức nguyên mẫuArray.includes, String.startsWith
Đối tượng toàn cụcPromise, Map, Set, Symbol
Phương thức tĩnhObject.assign, Array.from
Cú pháp ngôn ngữArrow functions, async/await, class
Web APIfetch, IntersectionObserverService Worker (yêu cầu hỗ trợ gốc)

Polyfill vs transpilation: khác biệt và tương tác

Transpilation chuyển đổi cú pháp mới thành cú pháp cũ (const → var, () => {} → function() {}). Polyfill thêm các phương thức và đối tượng bị thiếu (Promise, Array.includes). Hai cơ chế này bổ sung cho nhau: transpilation làm cho mã tương thích về mặt cú pháp, polyfill đảm bảo tính đầy đủ của API. Babel + core-js là sự kết hợp tiêu chuẩn để hỗ trợ đầy đủ.

Babel @babel/preset-env với tùy chọn useBuiltIns xác định polyfill nào cần thiết dựa trên trình duyệt mục tiêu. useBuiltIns: "usage" phân tích API nào được sử dụng trong mã và chỉ nhập các polyfill cần thiết từ core-js. useBuiltIns: "entry" nhập tất cả polyfill cho trình duyệt mục tiêu thông qua một lần nhập core-js/stable duy nhất.

Ví dụ: polyfill cho Array.prototype.includes

js
// Kiểm tra sự tồn tại và thêm polyfill
if (typeof Array.prototype.includes !== "function") {
  Object.defineProperty(Array.prototype, "includes", {
    value: function(searchElement, fromIndex) {
      if (this == null) {
        throw new TypeError("Array.prototype.includes called on null or undefined");
      }
      var arr = Object(this);
      var len = arr.length >>> 0;
      if (len === 0) { return false; }
      var start = fromIndex | 0;
      var k = Math.max(start >= 0 ? start : len + start, 0);

      while (k < len) {
        if (arr[k] === searchElement) { return true; }
        k++;
      }
      return false;
    },
    writable: true,
    configurable: true,
  });
}

// Sử dụng — nay an toàn trên mọi trình duyệt
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true

Polyfill cho Array.prototype.includes kiểm tra xem phương thức đã được định nghĩa trên nguyên mẫu Array chưa. Nếu chưa, nó tạo thuộc tính qua Object.defineProperty với các cờ writable: true, configurable: true. Việc triển khai tuân theo đặc tả ES2016: kiểm tra null/undefined, chuyển đổi thành đối tượng, xử lý fromIndex âm. Sau khi thêm polyfill, lệnh gọi arr.includes(3) hoạt động trong tất cả trình duyệt, bao gồm Internet Explorer 11.

core-js: thư viện polyfill tiêu chuẩn

core-js là thư viện polyfill JavaScript toàn diện nhất, hỗ trợ tất cả các đề xuất stage-4 của TC39 (tiêu chuẩn ECMAScript). core-js bao gồm polyfill cho Promise, Symbol, Map, Set, WeakMap, WeakSet, phương thức Array, phương thức String, phương thức Object, phương thức Number, phương thức Math, Reflect, globalThis và tất cả đề xuất stage-4. Phiên bản hiện tại core-js 3.38+ bao phủ ES5–ES2025.

core-js tích hợp với Babel thông qua @babel/preset-env và tùy chọn useBuiltIns. Nếu không có tích hợp này, nhà phát triển phải nhập thủ công từng polyfill: import "core-js/stable/array/includes". @babel/preset-env tự động thêm các import cần thiết dựa trên trình duyệt mục tiêu từ .browserslistrc. Điều này giảm kích thước bundle — chỉ bao gồm các polyfill cần thiết.

Ví dụ: polyfill cho fetch

Fetch API là một trong những Web API được polyfill thường xuyên nhất. Triển khai fetch gốc có sẵn trong Chrome 42+ (2015), Safari 10.1+ (2017), Firefox 39+ (2015), nhưng không có trong Internet Explorer và WebView cũ. Polyfill whatwg-fetch mô phỏng fetch qua XMLHttpRequest. Giải pháp thay thế — sử dụng isomorphic-fetch (polyfill cho Node.js và trình duyệt) hoặc thư viện phổ quát axios, không yêu cầu polyfill.

js
// Tải polyfill fetch chỉ cho trình duyệt cũ
if (typeof self.fetch !== "function") {
  import("whatwg-fetch").then(module => {
    self.fetch = module.fetch;
    console.log("fetch polyfill loaded");
  });
}

// Sử dụng fetch (hoạt động với cả polyfill và API gốc)
async function loadData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const json = await response.json();
    return json;
  } catch (error) {
    console.error("Failed to load:", error);
  }
}

Nhập động polyfill fetch qua import() đảm bảo rằng trình duyệt hiện đại không tải mã không cần thiết. Polyfill được tải bất đồng bộ và không chặn luồng chính. Sau khi tải, self.fetch thay thế triển khai gốc hoặc thêm triển khai bị thiếu. Đây là kỹ thuật cải tiến dần dần: trình duyệt hiện đại chỉ nhận mã gốc, trình duyệt cũ nhận thêm polyfill.

Tích hợp core-js với Babel

js
// babel.config.js — core-js + preset-env
module.exports = {
  presets: [
    ["@babel/preset-env", {
      useBuiltIns: "usage",
      corejs: {
        version: "3.38",
        proposals: true,
      },
      targets: {
        browsers: ["> 0.5%", "not dead", "not op_mini all"],
      },
    }],
  ],
};
none
# .browserslistrc — trình duyệt mục tiêu
> 0.5%
last 2 versions
not dead
not op_mini all
ie >= 11
not ios_saf < 12

useBuiltIns: "usage" phân tích mã và chỉ thêm các polyfill thực sự được sử dụng. corejs.version chỉ định phiên bản core-js trong dự án. targets.browsers xác định mức trình duyệt tối thiểu — trình duyệt mục tiêu càng cũ, càng nhiều polyfill được bao gồm. .browserslistrc được sử dụng không chỉ bởi Babel mà còn bởi Autoprefixer, PostCSS và Stylelint để nhắm mục tiêu nhất quán.

Polyfill.io và tải polyfill động

Polyfill.io là một dịch vụ (và thư viện cùng tên) xác định động những polyfill nào trình duyệt người dùng cần và chỉ trả về những polyfill đó. Polyfill.io sử dụng tiêu đề User-Agent để xác định phiên bản trình duyệt và cung cấp bộ polyfill tối thiểu. Điều này giảm lượng dữ liệu truyền tải so với gói polyfill phổ quát.

Kết nối Polyfill.io được thực hiện qua thẻ <script> trước mã ứng dụng chính. Dịch vụ phân tích User-Agent và trả về tệp JavaScript chỉ chứa polyfill cho trình duyệt đó. Chrome sẽ không nhận được polyfill nào, IE 11 sẽ nhận được bộ đầy đủ. Đây là cách tiếp cận tối ưu cho hiệu suất: trình duyệt hiện đại không tải mã không cần thiết.

Kết nối Polyfill.io

html
<!-- Polyfill.io: tải động -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Promise%2CArray.prototype.includes%2CObject.assign%2Cfetch"></script>

<!-- Phiên bản Polyfill.io cục bộ -->
<script src="/js/polyfill.js"></script>
<script>
  // feature detection for fetch
  if (!self.fetch) {
    loadScript("/js/fetch-polyfill.js");
  }
</script>

Tham số features trong URL Polyfill.io chỉ định polyfill nào cần tải. Giá trị khả dụng: tên phương thức (Array.prototype.includes), đối tượng toàn cục (Promise) hoặc cờ (es6, es2016). Cờ "default" tải bộ cơ bản cho JavaScript hiện đại. Đối với dự án sản xuất, nên lưu trữ Polyfill.io trên CDN riêng hoặc sử dụng phiên bản địa phương của thư viện để kiểm soát tính khả dụng.

Polyfill trong ứng dụng di động và WebView

WebView trong ứng dụng di động (Android WebView, WKWebView trên iOS) là môi trường đặc biệt cho polyfill. Phiên bản WebView phụ thuộc vào phiên bản hệ điều hành và bản cập nhật Chrome System WebView đã cài đặt (Android) hoặc WKWebView từ iOS Safari. Trong các phiên bản Android cũ (4.4, 5.0), WebView dựa trên Chromium 30–37 — không hỗ trợ fetch, Promise, IntersectionObserver.

React Native sử dụng JavaScriptCore (iOS) hoặc Hermes (Android) — các engine này triển khai ES6+ khác nhau. JavaScriptCore trên iOS hỗ trợ hầu hết các tính năng ES6 nhưng có thể thiếu một số đề xuất stage-3. Hermes (được sử dụng mặc định trong React Native 0.70+) hỗ trợ bộ giới hạn của tiêu chuẩn ES — polyfill là bắt buộc cho nó.

Kiểm tra hỗ trợ trong WebView

js
// feature detection cho WebView
const polyfills = [];

// Promise
if (typeof Promise === "undefined") {
  polyfills.push("Promise");
}

// Fetch API
if (typeof self.fetch === "undefined") {
  polyfills.push("fetch");
}

// IntersectionObserver (cần cho tải lười)
if (typeof IntersectionObserver === "undefined") {
  polyfills.push("IntersectionObserver");
}

// Tải polyfill động
if (polyfills.length > 0) {
  const script = document.createElement("script");
  script.src = "https://cdn.polyfill.io/v3/polyfill.min.js"
    + "?features=" + polyfills.join(",");
  document.head.appendChild(script);
}

Phát hiện tính năng cho WebView kiểm tra sự hiện diện của các API quan trọng (Promise, fetch, IntersectionObserver) và tải động polyfill chỉ cho những API bị thiếu. Điều này đảm bảo WebView hiện đại (Chrome 100+ trên Android 12) không tải mã không cần thiết, trong khi WebView cũ (Android 5.0) nhận được hỗ trợ cần thiết.

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

Có cần polyfill cho React Native không?

React Native trên Hermes yêu cầu polyfill cho một số phương thức ES: Array.flat, Array.flatMap, globalThis, TextEncoder. Nên bao gồm core-js hoặc react-native-polyfill-globals cho bản build sản xuất. JavaScriptCore trên iOS hỗ trợ nhiều tính năng hơn, nhưng cũng có thể yêu cầu polyfill cho các đề xuất stage-3.

Polyfill có ảnh hưởng đến hiệu suất không?

Polyfill làm giảm hiệu suất 1–5%, vì triển khai JavaScript chậm hơn triển khai C++ gốc trong engine. Ví dụ: polyfill Promise trong JS thuần chậm hơn Promise gốc trong V8. Tuy nhiên, đối với hầu hết ứng dụng, sự khác biệt là không đáng kể. Đối với mã quan trọng, nên kiểm tra triển khai gốc thông qua phát hiện tính năng.

Polyfill khác transpilation như thế nào?

Transpilation chuyển đổi cú pháp: const → var, hàm mũi tên → function. Polyfill thêm đối tượng/phương thức mới: Promise, Array.includes, fetch. Transpilation hoạt động tại thời điểm build, polyfill được tải trong thời gian chạy. Cả hai cơ chế đều cần thiết để hỗ trợ đầy đủ mã hiện đại trong môi trường cũ.

Có thể tránh polyfill vào năm 2026 không?

Có thể, nếu đối tượng mục tiêu chỉ sử dụng trình duyệt hiện đại (Chrome 90+, Safari 15+, Firefox 90+). Đối với dự án hỗ trợ thiết bị cũ hoặc người dùng doanh nghiệp (Internet Explorer 11 vẫn được sử dụng trong khu vực chính phủ), polyfill là bắt buộc. Phân tích thống kê trình duyệt của khán giả qua Google Analytics.

Kích thước gói polyfill core-js là bao nhiêu?

core-js trong bản build đầy đủ nặng ~85 KB (gzip). Khi sử dụng useBuiltIns: "usage" trong Babel, chỉ bao gồm các polyfill cần thiết, giảm kích thước xuống 5–30 KB tùy theo trình duyệt mục tiêu. Đối với trình duyệt hiện đại (Chrome 100+), có thể không cần polyfill nào.

Tóm tắt

  • Polyfill là sự mô phỏng API bị thiếu trong môi trường thực thi, đảm bảo tương thích mã hiện đại với môi trường cũ
  • core-js là thư viện polyfill tiêu chuẩn cho ES5–ES2025, tích hợp với Babel qua @babel/preset-env
  • Polyfill.io là dịch vụ tải polyfill động dựa trên User-Agent của trình duyệt
  • Transpilation + polyfill là giải pháp toàn diện: Babel biến đổi cú pháp, core-js thêm API bị thiếu
  • Phát hiện tính năng kiểm tra triển khai gốc trước khi tải polyfill để tối ưu hiệu suất
  • WebView và Hermes yêu cầu polyfill bắt buộc cho fetch, Promise và IntersectionObserver trong phiên bản cũ

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm