shim与polyfill的区别

3,422 阅读2分钟

1.前言

最近在学习babel的时候,遇见了polyfill这个概念。虽然之间听说过,但是一直搞不清shimpolyfill这两个学术用语之间到底是什么区别?谷歌了许多资料,但是大多数都是晦涩难懂的长篇大论或者照抄照搬。于是我分别找了一个shim库与polyfill库来看。分别是es6-shimes6-promise。这两个库的链接地址会放在文章末尾。现在就针对这两个库的代码,来认识下shimpolyfill的功能定义上有何不同。

2.shim

分析了下es6-shim的源码,现在把整体逻辑梳理为下面代码:

// UMD
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(factory);
  } else if (typeof exports === 'object') {
    module.exports = factory();
  } else {
    // Browser globals (root is window)
    root.returnExports = factory();
  }
}(this, function () {
  'use strict';
  // 判断并获取全局对象
  var getGlobal = function () {
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
  }
  // 得到环境下的全局对象
  var globals = getGlobal();
  // es6 Map
  globals.Map = ...
  // es6 Set
  globals.Set = ...
  // es6 Symbol
  globals.Symbol = ...
  
  // 将全局对象返回,当es6-shim库被引入时,会自动在全局对象挂载API,也就是重新造了一个新环境。
  return globals
}))

说明:当引入shim库时,它会影响全局对象。通过对全局对象增加API,从而创建一个新的环境。shim不会判断API是否已经存在。

3.polyfill

es6-promisepolyfill代码逻辑:

import Promise from './promise';

export default function polyfill() {
  let local;
  if (typeof global !== 'undefined') {
    local = global;
  } else if (typeof self !== 'undefined') {
    local = self;
  } else {
    try {
      local = Function('return this')();
    } catch (e) {
      throw new Error('polyfill failed because global object is unavailable in this environment');
    }
  }

  let P = local.Promise;
    // 这里判断了全局对象下是否有Promise这个属性。   
  if (P) {
    var promiseToString = null;
    try {
      promiseToString = Object.prototype.toString.call(P.resolve());
    } catch(e) {
      ...
    }
    if (promiseToString === '[object Promise]' && !P.cast){
        // 如果已经有了Promise,就直接返回,不会再给全局对象增加Promise属性。
      return;
    }
  }
    // 代码执行到这里,给全局对象添加Promise的API
  local.Promise = Promise;
}

说明:从上面的源码,可以看出,polyfill库与shim库大致逻辑基本一致。但是有所不同的是,polyfill会先判断当前环境是否已经有了目标API,如果有了,会停止,没有时才会添加polyfill实现的API。

3.总结

由上面的分析对比,可以知道的是,shim针对的是环境,polyfill针对的是API

在使用shim时,不会在意旧环境是否已经存在某API,它会直接重新改变全局对象,为旧环境提供API(它是一个垫片,为旧环境提供新功能,从而创建一个新环境)。

而在polyfill中,它会判断旧环境是否已经存在API,不存在时才会添加新API(它是腻子,抹平不同环境下的API差异)。

这二者的目的并不相同。

4.源码链接

es6-shim

es6-promise