玖叶教程网

前端编程开发入门

shim vs. sham vs. polyfill! 前端都要懂!

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

高级前端进阶

今天给大家带来的主题是前端基础知识,即shim、sham和polyfill。话不多说,直接开始!

1.shim 和 sham

1.1 什么是 shim

在计算机编程中,shim 是一个库,它可以透明地拦截 API 调用并更改传递的参数,自行处理操作或将操作重定向到其他地方。 shim 可用于支持较新环境中的旧 API,或旧环境中的新 API。 shim 还可以用于在与开发它们不同的软件平台上运行程序。

旧 API 的 shim 通常在 API 的行为发生变化时出现,从而避免仍然依赖旧功能的旧应用程序出现兼容性问题; 在这种情况下,较旧的 API 仍然可以通过位于较新代码之上的兼容层来支持。 用于较新 API 的 shim 被定义为:“仅使用该环境的手段将新 API 引入旧环境的库”。

直白地说,shim 表示通过猴子补丁(monkey-patch)来模拟真实的 ES5 行为。换句话说:shim 可以让开发者像使用 ES5 一样使用这些文件提供的功能,但是通常是通过添加新的 API 来实现。

1.2 什么是 sham

sham,发音为/[??m]/,中文翻译为“虚假的, 假装的”。sham 包含那些无法用其他代码模拟的功能(比如有些 ES5/ES6 的 API 无法用 ES3 模拟), 它们基本上只提供 API,保证代码执行不会导致应用崩溃,但可以不包含实际的功能。以 Github 上周下载量达到 4200W+的 has-symbols 的 sham 为例,其主要用于判断代码执行环境是否支持 ES6 的 Symbol。

// shams.js
'use strict';
module.exports = function hasSymbols() {
  if (
    typeof Symbol !== 'function' ||
    typeof Object.getOwnPropertySymbols !== 'function'
  ) {
    return false;
  }
  if (typeof Symbol.iterator === 'symbol') {
    return true;
  }
  var obj = {};
  var sym = Symbol('test');
  var symObj = Object(sym);
  if (typeof sym === 'string') {
    return false;
  }
  if (Object.prototype.toString.call(sym) !== '[object Symbol]') {
    return false;
  }
  if (Object.prototype.toString.call(symObj) !== '[object Symbol]') {
    return false;
  }
  var symVal = 42;
  obj[sym] = symVal;
  for (sym in obj) {
    return false;
  }
  if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) {
    return false;
  }
  if (
    typeof Object.getOwnPropertyNames === 'function' &&
    Object.getOwnPropertyNames(obj).length !== 0
  ) {
    return false;
  }
  var syms = Object.getOwnPropertySymbols(obj);
  if (syms.length !== 1 || syms[0] !== sym) {
    return false;
  }
  if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) {
    return false;
  }
  if (typeof Object.getOwnPropertyDescriptor === 'function') {
    var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
    if (descriptor.value !== symVal || descriptor.enumerable !== true) {
      return false;
    }
  }
  return true;
};

下面是 has-symbols 这个 sham 的入口代码:

'use strict';
var origSymbol = typeof Symbol !== 'undefined' && Symbol;
var hasSymbolSham = require('./shams');
// 导入上面的shams.js文件
module.exports = function hasNativeSymbols() {
  if (typeof origSymbol !== 'function') {
    return false;
  }
  if (typeof Symbol !== 'function') {
    return false;
  }
  if (typeof origSymbol('foo') !== 'symbol') {
    return false;
  }
  if (typeof Symbol('bar') !== 'symbol') {
    return false;
  }

  return hasSymbolSham();
};

这个方法首先判断是否存在原生的 Symbol,如果没有则直接返回 false,否则调用 shams.js 继续判断。下面是直接调用这个库的示例代码:

var hasSymbols = require('has-symbols');

hasSymbols() === true;
// 如果原生环境支持Symbol,返回true。 不可填充,不可伪造。
var hasSymbolsKinda = require('has-symbols/shams');
hasSymbolsKinda() === true;
// 如果执行环境有一个遵循规范的 Symbol sham,则返回true

使用 shim 还是 sham 则依赖于具体的编码方式。如果要使用 shim 提供的完整功能就包含 shim 就好,如果还使用 sham 包含的特性,则需要包含两者。

2.典型示例

2.1 es5-shim

es5-shim.js 和 es5-shim.min.js 通过打猴子补丁的方式弥补 JavaScript 上下文以包含所有 EcmaScript 5 常见方法,使得开发者编写的代码就像在真实的 ES5 环境运行一样。

但是需要注意的是,由于 es5-shim.js 是为了给原生 Javascript 引擎打补丁,所以它应该是最先加载的库。

es5-shim 的主要功能是为了允许将代码写入 ES5 而不会在旧引擎中导致运行时错误。 虽然这意味着很多 ES5 方法在旧引擎中可能会悄无声息地执行失败,但是却不至于导致整个应用程序崩溃。 注意:es5-sham.js 需要 es5-shim.js 才能正常工作。目前 es5-shim 包含的 shim 包括:

Array.prototype.every (standalone shim)
Array.prototype.filter (standalone shim)
Array.prototype.forEach (standalone shim)
Array.prototype.indexOf (standalone shim)
// 更多数组方法
Date.now
Date.prototype.toJSON
// 更多 Date 方法
Number.prototype.toExponential (standalone shim)
Number.prototype.toFixed
Number.prototype.toPrecision
// 更多 Number 方法
String.prototype.split (standalone shim)
String.prototype.trim (standalone shim)
String.prototype.lastIndexOf (standalone shim)
String.prototype.replace
// 更多 String 方法
Error.prototype.toString
Error.prototype.name
Error.prototype.message
// 更多 Error 方法

es5-shim 包含的 sham 包括:

Object.create;
Object.getPrototypeOf;
Object.getOwnPropertyNames;
Object.isSealed;
Object.isFrozen;
Object.isExtensible;
Object.getOwnPropertyDescriptor;
Object.defineProperty;
// 更多其他方法

如果需要在浏览器使用,直接引入下面代码即可:


<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.14/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.14/es5-sham.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.5/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.5/es6-sham.min.js"></script>
<script src="other-libs.js"></script>

目前 es5-shim 的 NPM 周下载量稳定在 3443K,Github 上 star 超过 7.1K,fork 达到了 1K。

2.2 es6-shim

提供兼容性垫片,以便遗留 JavaScript 引擎的行为尽可能接近 ECMAScript 6 (Harmony)。es6-shim 提供的 Sham 方法包括:

  • Map
  • Set
  • Promise
  • String
  • String.prototype
  • RegExp
  • RegExp.prototype
  • Reflect
  • Symbol
  • 更多其他方法

Map、Set 和 Promise 实现是可继承的,开发者可以使用以下模式在 ES5 中创建一个子类,该子类将继续在 ES6 中工作:

require('es6-shim');

function MyPromise(exec) {
  var promise = new Promise(exec);
  Object.setPrototypeOf(promise, MyPromise.prototype);
  // ...
  return promise;
}
Object.setPrototypeOf(MyPromise, Promise);
MyPromise.prototype = Object.create(Promise.prototype, {
  constructor: { value: MyPromise },
});

目前 es6-shim 的 NPM 周下载量稳定在 4,467K,Github 上 star 超过 3.1K,fork 达到了 400+。

3.shim vs polyfill

Polyfill 是 Shim 的下一个版本或高级版本, Polyfill 是在 API 调用中实现遗失特性的概念。 Polyfill 是一段代码或一个插件,它提供了作为开发人员希望 Web 浏览器原生支持的技术,Polyfill 为传统浏览器添加了现代 HTML5/CSS3 实现。

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.js"></script>

引入上面的 polyfill 后,es6、es7 特性就可以随便写了。但缺点是,babel-polyfill 包含所有补丁,不管浏览器是否支持,也不管你的项目是否有用到,都全量引入,所以如果用户不差流量和带宽(比如内部应用),尽可以用这种方式。

shim 和 polyfill 主要差异可以概括为以下几个点:

  • Shim 是一个简单的库,可将较新的 API 引入较旧的平台环境。Polyfill 是 Web 浏览器 API 的 Shim,它检查浏览器是否支持 API
  • Shim 代码可以添加 JavaScript 代码来修复某些功能,但它通常会有自己的 API。Polyfill 也是开发者可以加入的东西(即 JavaScript),它会默默地模仿不受支持的浏览器 API。
  • Shim 不会在 API 调用中安装缺少的功能,但是 Polyfill 会默默安装自己的实现。
  • Shim 只能纠正已经存在的代码的行为,Polyfill 在不支持该功能的 Web 浏览器上实现了功能,无论该功能是否存在。

4.本文总结

本文主要和大家介绍前端都要懂的 shim、sham 、Polyfill以及三者之间的区别。因为篇幅有限,文章并没有过多展开,如果有兴趣,可以直接在我主页继续阅读,但是文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!

参考资料

https://itecnote.com/tecnote/javascript-shim-vs-sham-what-is-the-difference/

https://github.com/es-shims/es5-shim

https://www.geeksforgeeks.org/what-is-the-difference-between-a-shim-and-a-polyfill/

https://en.wikipedia.org/wiki/Shim_(computing)

https://www.npmjs.com/package/es6-shim

https://www.npmjs.com/package/has-symbols

https://www.jianshu.com/p/ace033343034/

https://github.com/sorrycc/blog/issues/80

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言