Polyfill:定义、工作原理以及API模拟库

作者: IT Sectr 发布日期: 2026-05-19 阅读时间: 9 分钟

Polyfill(polyfill)——在未原生实现的功能(API、方法、对象)的环境中模拟这些功能的代码。Polyfill允许在旧版浏览器和运行时环境中使用现代JavaScript、CSS或Web API功能。根据MDN Web Docs,polyfill是渐进增强和确保跨浏览器兼容性的关键工具。

要点

  • Polyfill——在未实现API的执行环境中对该API进行软件模拟
  • core-js——现代JavaScript的标准polyfill库,支持所有stage-4提案
  • Polyfill.io——仅根据用户浏览器动态提供polyfill的服务
  • 转译 vs polyfill:转译转换语法(箭头函数→function),polyfill添加新方法(Array.includes、Promise)
  • Feature detection——在加载polyfill前检查原生实现是否存在以避免冲突

什么是Polyfill?

Polyfill——实现执行环境未原生支持功能的代码片段(通常是JavaScript)。该术语由Remy Sharp于2009年作为一个文字游戏提出:Polyfill——类比填缝剂(Polyfilla),填补墙壁上的裂缝。Polyfill填补了标准与特定浏览器或运行时环境支持之间的空白。

Polyfill不修改现有代码——它扩展执行环境。如果浏览器不支持Array.prototype.includes,polyfill会在主代码执行前将此方法添加到Array原型中。Polyfill可以模拟新的全局对象(Promise、Map、Set、Symbol)、静态方法(Array.from、Object.assign)和原型方法。

Feature detection——安装polyfill前的必要机制。不应检查user-agent(什么浏览器),而应检查方法是否存在:if (!Array.prototype.includes) { Array.prototype.includes = ... }。这保证了polyfill不会覆盖已存在的原生实现。Google Analytics和其他服务收集API支持数据用于分析。

Polyfill何时出现

首批polyfill出现在Internet Explorer 6–8时代(2005–2009),当时开发人员发现了W3C标准与浏览器实现之间的差距。该术语由Remy Sharp于2009年在BarCamp London会议上引入。第一个大规模polyfill是html5shiv(2009)——为Internet Explorer添加HTML5标签(<section>、<article>、<nav>)支持的库。

随着ES6(2015)的出现和ECMAScript的年度更新周期,所需polyfill的数量增加了。每年标准都会添加旧浏览器不支持的新方法(Array.includes、String.padStart、Object.fromEntries、Promise.allSettled)。2014年以es6-shim起步的core-js成为了通用解决方案。到2026年,core-js包含超过5000个针对ES5–ES2025的polyfill模块。

可以polyfill和不可以polyfill的内容

类别可以polyfill不可以polyfill
原型方法Array.includes、String.startsWith
全局对象Promise、Map、Set、Symbol
静态方法Object.assign、Array.from
语言语法箭头函数、async/await、class
Web APIfetch、IntersectionObserverService Worker(需要原生支持)

Polyfill vs 转译:区别与相互作用

转译将新语法转换为旧语法(const → var、() => {} → function() {})。Polyfill添加缺失的方法和对象(Promise、Array.includes)。这两种机制相辅相成:转译确保语法兼容性,polyfill确保API的完整性。Babel + core-js是全面支持的标准组合。

Babel @babel/preset-env配合useBuiltIns选项根据目标浏览器确定需要哪些polyfill。useBuiltIns: “usage”分析代码中使用了哪些API,只从core-js连接所需的polyfill。useBuiltIns: “entry”通过单个core-js/stable导入连接目标浏览器的所有polyfill。

示例:Array.prototype.includes的polyfill

js
// 检查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,
  });
}

// 使用——现在在任何浏览器中都是安全的
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true

Array.prototype.includes的polyfill检查该方法是否在Array原型中定义。如果没有——通过Object.defineProperty创建属性,设置writable: true、configurable: true标志。实现遵循ES2016规范:检查null/undefined、转换为对象、处理负值的fromIndex。添加polyfill后,arr.includes(3)调用在所有浏览器中均有效,包括Internet Explorer 11。

core-js:标准polyfill库

core-js——最完整的JavaScript polyfill库,支持所有TC39 stage-4提案(ECMAScript标准)。core-js包含Promise、Symbol、Map、Set、WeakMap、WeakSet、Array方法、String方法、Object方法、Number方法、Math方法、Reflect、globalThis及所有stage-4 proposals的polyfill。当前版本core-js 3.38+覆盖ES5–ES2025。

core-js通过@babel/preset-env和useBuiltIns选项与Babel集成。没有这种集成,开发人员必须手动导入每个polyfill:import “core-js/stable/array/includes”。@babel/preset-env根据.browserslistrc中的目标浏览器自动添加必要的导入。这减少了打包体积——只连接所需的polyfill。

示例:fetch的polyfill

Fetch API——最常被polyfill的Web API之一。fetch的原生实现在Chrome 42+(2015)、Safari 10.1+(2017)、Firefox 39+(2015)中可用,但在Internet Explorer和旧版WebView中缺失。whatwg-fetch polyfill通过XMLHttpRequest模拟fetch。替代方案——使用isomorphic-fetch(针对Node.js和浏览器的polyfill)或不需要polyfill的通用axios库。

js
// 仅针对旧浏览器加载fetch polyfill
if (typeof self.fetch !== "function") {
  import("whatwg-fetch").then(module => {
    self.fetch = module.fetch;
    console.log("fetch polyfill loaded");
  });
}

// 使用fetch(与polyfill和原生API均兼容)
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);
  }
}

通过import()动态导入fetch polyfill确保现代浏览器不加载多余代码。Polyfill异步加载,不阻塞主线程。加载后,self.fetch替换原生实现或添加缺失的实现。这是一种渐进增强技术:现代浏览器只获得原生代码,旧浏览器获得额外的polyfill。

core-js与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 — 目标浏览器
> 0.5%
last 2 versions
not dead
not op_mini all
ie >= 11
not ios_saf < 12

useBuiltIns: “usage”分析代码,只添加实际使用的polyfill。corejs.version指示项目中core-js的版本。targets.browsers确定浏览器的最低级别——浏览器越老,连接的polyfill越多。.browserslistrc不仅被Babel使用,还被Autoprefixer、PostCSS和Stylelint用于一致的目标定位。

Polyfill.io与polyfill的动态加载

Polyfill.io——一种服务(和同名的库),动态确定用户浏览器需要哪些polyfill并只返回这些polyfill。Polyfill.io使用User-Agent头部确定浏览器版本,并提供最少的polyfill集。与通用的polyfill打包相比,这减少了传输的数据量。

通过应用程序主代码前的<script>标签连接Polyfill.io。服务分析User-Agent并返回仅包含该浏览器所需polyfill的JavaScript文件。Chrome不会获得任何polyfill,IE 11将获得完整集合。这是性能最佳的方法:现代浏览器不加载多余代码。

连接Polyfill.io

html
<!-- Polyfill.io:动态加载 -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Promise%2CArray.prototype.includes%2CObject.assign%2Cfetch"></script>

<!-- Polyfill.io的本地版本 -->
<script src="/js/polyfill.js"></script>
<script>
  // 针对fetch的特性检测
  if (!self.fetch) {
    loadScript("/js/fetch-polyfill.js");
  }
</script>

URL中的features参数指定要加载哪些polyfill。可能的值包括:方法名(Array.prototype.includes)、全局对象(Promise)或标志(es6、es2016)。“default”标志为现代JavaScript连接基础集。对于生产项目,建议在自己的CDN上托管Polyfill.io或使用库的本地版本以控制可用性。

移动应用和WebView中的polyfill

WebView在移动应用中(Android WebView、iOS上的WKWebView)——一个特殊的polyfill环境。WebView版本取决于操作系统版本和已安装的Chrome System WebView(Android)或iOS Safari的WKWebView更新。在旧版Android(4.4、5.0)中,WebView基于Chromium 30–37——不支持fetch、Promise、IntersectionObserver。

React Native使用JavaScriptCore(iOS)或Hermes(Android)——这些引擎以不同方式实现ES6+。iOS上的JavaScriptCore支持大多数ES6功能,但可能缺少某些stage-3提案。Hermes(React Native 0.70+中默认使用)支持有限的ES标准集——对于它,polyfill是必需的。

在WebView中检查支持

js
// 针对WebView的特性检测
const polyfills = [];

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

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

// IntersectionObserver(用于懒加载)
if (typeof IntersectionObserver === "undefined") {
  polyfills.push("IntersectionObserver");
}

// Polyfill的动态加载
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);
}

针对WebView的Feature detection检查关键API(Promise、fetch、IntersectionObserver)是否存在,并只为缺失的API动态加载polyfill。这确保现代WebView(Android 12上的Chrome 100+)不加载多余代码,而旧版WebView(Android 5.0)获得必要的支持。

常见问题

React Native需要polyfill吗?

React Native在Hermes上需要对某些ES方法进行polyfill:Array.flat、Array.flatMap、globalThis、TextEncoder。建议为生产构建连接core-js或react-native-polyfill-globals。iOS上的JavaScriptCore支持更多功能,但也可能需要对stage-3提案进行polyfill。

Polyfill是否影响性能?

Polyfill会将性能降低1–5%,因为JavaScript实现比引擎中原生的C++实现更慢。例如,纯JS中的polyfill Promise比V8中原生Promise慢。但对于大多数应用而言,差异并不明显。对于关键代码,建议通过feature detection检查原生实现是否存在。

Polyfill与转译有何区别?

转译转换语法:const → var、箭头函数 → function。Polyfill添加新对象/方法:Promise、Array.includes、fetch。转译在构建阶段工作,polyfill在运行时加载。这两种机制对于在旧环境中全面支持现代代码都是必需的。

在2026年可以不使用polyfill吗?

可以,如果目标受众只使用现代浏览器(Chrome 90+、Safari 15+、Firefox 90+)。对于支持旧设备或企业用户(Internet Explorer 11仍在政府部门使用)的项目,polyfill是必需的。通过Google Analytics分析受众的浏览器统计信息。

core-js polyfill的体积有多大?

core-js完整构建约~85 KB(gzip)。在Babel中使用useBuiltIns: “usage”时,只连接所需的polyfill,根据目标浏览器将体积减小到5–30 KB。对于现代浏览器(Chrome 100+),可能不需要任何polyfill。

总结

  • Polyfill——在执行环境中模拟缺失的API,确保现代代码与旧环境的兼容性
  • core-js——ES5–ES2025的标准polyfill库,通过@babel/preset-env与Babel集成
  • Polyfill.io——基于浏览器User-Agent动态加载polyfill的服务
  • 转译 + polyfill——综合解决方案:Babel转换语法,core-js添加缺失的API
  • Feature detection——在加载polyfill前检查原生实现是否存在以提升性能
  • WebView和Hermes在旧版本中需要对fetch、Promise和IntersectionObserver进行强制polyfill

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读