Search code examples
javascriptwebpacklodash

Webpack and global scope pollution


I'm following Webpack guide. I imported lodash like this:

import _ from 'lodash';

It works as expected, but there is something unclear. Guide claims that global scope won't be affected with lodash in this case, while it appears to not be true.

In this setup, index.js explicitly requires lodash to be present, and binds it as _ (no global scope pollution). By stating what dependencies a module needs, webpack can use this information to build a dependency graph. It then uses the graph to generate an optimized bundle where scripts will be executed in the correct order.

In Chrome's console I still can access lodash:

console.log(_, _.join, window._.join);

All of these work, so it's pretty obvious lodash is in fact polluting the global scope.

Is it because lodash did explicitly assign itself to window or is it something else?


Solution

  • Looking at lodash v4.17.5 source code, it's apparent that lodash is adding _ to the global scope.

    /*--------------------------------------------------------------------------*/
    
      // Export lodash.
      var _ = runInContext();
    
      // Some AMD build optimizers, like r.js, check for condition patterns like:
      if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
        // Expose Lodash on the global object to prevent errors when Lodash is
        // loaded by a script tag in the presence of an AMD loader.
        // See http://requirejs.org/docs/errors.html#mismatch for more details.
        // Use `_.noConflict` to remove Lodash from the global object.
        root._ = _;
    
        // Define as an anonymous module so, through path mapping, it can be
        // referenced as the "underscore" module.
        define(function() {
          return _;
        });
      }
      // Check for `exports` after `define` in case a build optimizer adds it.
      else if (freeModule) {
        // Export for Node.js.
        (freeModule.exports = _)._ = _;
        // Export for CommonJS support.
        freeExports._ = _;
      }
      else {
        // Export to the global object.
        root._ = _;
      }
    }.call(this));
    

    That being said, it appears that you can use _.noConflict() to remove _ from the global scope.