In the following post I will compare immediately-invoked function expressions (IIFE) to Universal Module Definition (UMD) bundles.

IIFE

IIFE is a pattern you see a lot in JavaScript development. The main benefit is that it gives you a way to encapsulate code and create private scope. Basically it means creating a JavaScript closure and calling it immediately.

A very popular example of this is jQuery plugins.

In the following example I will show how to encapsulate a simple CarService in an IIFE:

export class CarService { getCarBrands() { return ['Ford', 'Mazda', 'Chrysler', 'Honda']; } }

Below is the same CareService wrapped in an IIFE.

(function (exports) { 'use strict'; class CarService { getCarBrands() { return ['Ford', 'Mazda', 'Chrysler', 'Honda']; } } exports.CarService = CarService; }((this.carModule_IIFE = this.carModule_IIFE || {})));

In addition to defining the CareService, the IIFE makes the service available via global scope by setting exports.CarService = CarService.

At runtime exports.CarService refers to global scope via this.carModule_IIFE.

We can now load the IIFE using a standard script tag like so:

<script src="dist/car-iife.js"></script>

After the script is loaded, carModule_IIFE.CarService is defined in global scope, and can be used to create instances of the services:

<script> var carServiceIIFE = new carModule_IIFE.CarService(); console.log(carServiceIIFE.getCarBrands()); </script>

Please note that the _IIFE suffix is just I name I chose for illustration purposes.

UMD

UMDs overlap with IIFEs. The main difference is that UMDs are much more flexible when it comes to supported module loading formats.

Next I will show how an UMD version of the original CareService can be used with different module patterns.

(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.carModule_UMD = global.carModule_UMD || {}))); }(this, (function (exports) { 'use strict'; class CarService { getCarBrands() { return ['Ford', 'Mazda', 'Chrysler', 'Honda']; } } exports.CarService = CarService; Object.defineProperty(exports, '__esModule', { value: true }); })));

At the top of the UMD you see several conditionals. These conditionals decide which module strategy to use based on the environment the code executes in.

Here are the different supported strategies:

Script Tag/Global Scope

The base case is to use an UMD the same way we used the IIFE.

<script src="dist/car-umd.js"></script> <script> var carServiceUMD = new carModule_UMD.CarService(); console.log(carServiceUMD.getCarBrands()); </script>

The approach here is the same as before.

CommonJS

The second supported pattern in UMD is commonJS. This is a pattern commonly seen in nodeJS.

In nodeJS the UMD bundle can be loaded using a require statement.

var CarService = require('./dist/car-umd').CarService; var carService = new CarService(); console.log(carService.getCarBrands());

AMD

The next supported pattern I will show is Asynchronous Module Definition (AMD).

AMD is a standard for supporting asynchronous module loading in JavaScript applications.

I will continue my CarService sample by showing how to do AMD based loading of the service. For the purposes of this sample I will be using the requireJS library to implement AMD.

<script data-main="dist/car-umd" src="node_modules/requirejs/require.js"></script> <script> require(["car-umd"], function(CarModuleAMD) { var carServiceAMD = new CarModuleAMD.CarService(); console.log(carServiceAMD.getCarBrands()); }); </script>

Please note, for all samples, the _UMD suffix is just I name I chose for illustration purposes.

As you can tell there is some overlap between IIFE and UMD, but UMD is much more flexible with support for many different module formats.