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à 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.
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.
| Danh mục | Có thể polyfill | Không thể polyfill |
|---|---|---|
| Phương thức nguyên mẫu | Array.includes, String.startsWith | — |
| Đối tượng toàn cục | Promise, Map, Set, Symbol | — |
| Phương thức tĩnh | Object.assign, Array.from | — |
| Cú pháp ngôn ngữ | — | Arrow functions, async/await, class |
| Web API | fetch, IntersectionObserver | Service Worker (yêu cầu hỗ trợ gố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.
// 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)); // truePolyfill 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 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.
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.
// 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.
// 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"],
},
}],
],
};# .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 < 12useBuiltIns: "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 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.
<!-- 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.
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ó.
// 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
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 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.
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ể, 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.
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
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.
Đọc thêm