I'm reading through some code from a library I recently downloaded to do matrix math for WebGL. However, I'm having a hard time wrapping my head around what this function does. This comes from the glMatrix.js library.
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.glMatrix = {}));
}(this, function (exports)
What is the factory parameter/function and where does exports come from? Can someone walk me step-by-step through what this function does?
EDIT: This is only the first part of the code which is why the opening bracket never closes.
This is a design pattern called UMD, it is a more advanced version of the IIFE design pattern. They both wrap your code, creating a new private scope. The main diferrence between the two is that UMD
is more abstract and will also work in node.js
and amd
rather than just browsers.
The first and last line you are showing in your question are basically the IIFE
part (minus the end since you have cropped it). You are invoking the iife
and passing the global
aka window
object and the factory
function which you can only see the function (exports)
part in your code.
The following part checks if the environment you are using is node, amd or regular JS
so that they will define the module in the each environmest requires i.e. node
just needs you to set the exports
object, amd
needs you to use the define
function and in vanilla JS
you just add the object to the window
or global
object.
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.glMatrix = {}));
In node and amd you don't need to name your export since you just need to require that file i.e. const glMatrix = require("./common.js");
, but in JS you need to retrieve from the global object which is why it's only one that requires to be named i.e. factory(global.glMatrix = {})
. That line adds the glMatrix
property to the global object (originally as an empty object) and then passes it as a parameter to your factory
function, which attaches all the functions, values and classes you should be able to access from outside the scope.
The implementation of the UMD
pattern may vary from library to library. For example here is a way you could do it without passing any parameters to the IIFE
(function() {
var global = this;
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.foo = {}));
function factory(exports) {
exports.bar = function() {
console.log("Hello World!");
};
}
})();
foo.bar();
In node you can just create a new file and export whatever you want. Node will then associate those exports with the file rather than a property (which is what happens in browser JS). So for example a file named foo.js
with the following contents:
function bar() {
console.log("Hello World!");
}
exports.bar = bar;
Can be accessed from another file like so:
const foo = require("foo.js");
foo.bar();
Or you could directly access the properties using Destructuring:
const { bar } = require("foo.js");
bar();