It took me a long time to understand this pattern at first, and I think it is mostly because of the way that it is written:
(function(root, factory) {
// Export MyModule depending on the environment
if (typeof define === "function" && define.amd) {
define("MyModule", [], factory);
} else {
root.MyModule = factory();
}
}(this, function() {
return {
// Module definition
};
}));
Isn't that the exact same thing as this?
(function(root) {
var factory = function() {
return {
// Module definition
};
};
// Export MyModule depending on the environment
if (typeof define === "function" && define.amd) {
define("MyModule", [], factory);
} else {
root.MyModule = factory();
}
}(this));
There is a var statement now, but I find this much easier to read. Am I missing something here? Is there a good reason to use the first approach?
Isn't that the exact same thing as this?
Not exactly - those root and factory variables are now in the scope of the module definition, it is one closure level deeper. And this has the grave disadvantage that factory cannot (trivially) be garbage-collected any more, while in the first pattern it was really anonymous.
I find this much easier to read
I disagree. AMD and UMD resolve around a single "define" wrapper that is called with a factory - where the contents of the factory function are the interesting ones (the "main" content of the file) and the definition call is nothing more but a header. This becomes especially obvious when the header is minified and takes only a single line.
In contrast, in the proposed alternative the module is hidden somewhere deep in that IEFE - it even needs to be indented one more level than normally.
Related
I was looking for a javascript implementation of the Astar algorithm that i could steal for my own project, and google immediately led me to bgrins implementation on github:
https://github.com/bgrins/javascript-astar
I copied and pasted his astar.js, and quickly found myself confused on how i was supposed to access the code from my own js module. I took a look at his demo.js file in their repo, and quickly saw that there were no imports. demo.js immediately has access to the astar function as well as the graph object that it takes as a parameter, and i am a little confused by how they managed to accomplish this.
On line 129 of demo.js, they have
this.graph = new Graph(nodes);
without ever using an import statement.
looking closer at the top of astar.js, i see the following code:
(function (definition) {
/* global module, define */
if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = definition();
} else if (typeof define === 'function' && define.amd) {
define([], definition);
} else {
var exports = definition();
window.astar = exports.astar;
window.Graph = exports.Graph;
}
})(function () {
Clearly setting window.astar and window.Graph are a part of the answer here, but I find this whole code block a little confusing and very abstract. Can anybody help me to understand what they are doing right here?
Hi i am trying to understand this steganography tool https://github.com/petereigenschink/steganography.js.
I am using this in my react project. The bit about RequireJS has me already confused. Can i turn this to a normal, not so scary javascript?
;(function (name, context, factory) {
// Supports UMD. AMD, CommonJS/Node.js and browser context
if (typeof module !== "undefined" && module.exports) {
module.exports = factory();
} else if (typeof define === "function" && define.amd) {
define(factory);
} else {
context[name] = factory();
}
})("steg", this, function () {}
I need to be able to use this function or whatever it is in my App.js file. Any pointers please?
This is the error that react dev server throws:
Failed to compile.
./src/steganography.js
Line 12: 'define' is not defined no-undef
Line 13: 'define' is not defined no-undef
Since javascript didn't originally include any way of doing modules (ie, to link together code from multiple files), several different approaches have been invented for ways to do so. That block of code is just trying to conform to all of them, so that the codebase will work regardless of which module approach the consumer is using.
The outer part is an Immediately Invoked Function Expression. This is an anonymous function which gets created and then immediately called. In this code, its main purpose is to isolate any variables inside the expression, and thus make it impossible for them to accidentally become global variables.
Inside the IIFE, it has some checks to figure out what kind of module system is being used, so that it can produce the correct output to be imported.
I want to grab browser version and OS from useragent via js - this is a practicing example (cause yes - I know - feature detection is how you do it properly ;).
I stumbled across a little library that does so and now I'm trying to understand whats going on.
See this Codepen: http://codepen.io/anon/pen/gPWZGE?editors=001
obviously by running bowser.version - you get the version extracted from the browsers useragent. However the function bowser itself is an anonymous function - even though I can access elements within this function which has to do with this part of the code
!function (name, definition) {
var module;
var define;
if (typeof module != 'undefined' && module.exports) module.exports = definition();
else if (typeof define == 'function' && define.amd) define(definition);
else this[name] = definition();
}
To be honest I have hardly any idea whats going on there - can someone please explain what those lines are doing?
Thanks a lot
Ok, so step by step ...
first you have an IIFE
!function( name, definition ) {
// some code
}( 'bowser', function(){
// your module code
});
this calls the first function immediately with two parameters: the name of the module (here "bowser") and the actual definition function. In that definition function you create the module: You define any objects, properties and whatever else it needs to work. Important point is: You interface (the view the "outside" has on your module, has to be returned by that defintion function (in your codepen this is line 282: return bowser;).
The outer function then determines, how to publish your module:
First it looks for a CommonJS environment:
typeof module != 'undefined' && module.exports
This would be used, e.g., inside NodeJS. If it found the respective objects, it will export your module as a CommonJS module: module.exports = definition().
If this was not a CommonJS environment, the script looks, if it is an AMD (for example, requireJS):
typeof define == 'function' && define.amd
Again, if that matches, the module is exported as a AMD module: define(definition).
Finally, if this is neither CommonJS nor AMD, the script assumes it is a (vanilla) browser or something similar and just attaches the module to the global namespace:
this[name] = definition();
Note, that each time the definition function is called and the return value is exported as a module. This is why your definition function has to return the module interface.
I just started learning patterns in JavaScript, and getting used to writing JavaScript like this:
(function(window){
var privateVar;
var privateFunc = function(param){
//do something
}
return{
publicFunc: function(){
do something
}
}(window));
But recently I found some scripts that write something like this in the beginning:
(function (root, factory) {
if ( typeof define === 'function' && define.amd ) {
define('something', factory(root));
} else if ( typeof exports === 'object' ) {
module.exports = factory(root);
} else {
root.something = factory(root);
}
})(window || this, function (root) {
var privateVar;
var privateFunc = function(param){
//do something
}
return{
publicFunc: function(){
do something
}
});
So, what does this piece of code in the beginning mean? What is the difference between that and with this module export technique:
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
TL;DR: JavaScript modules are loaded in different environments (different module loading systems, or no proper module system at all). The code you have is some boiler-plate code that lets you load a module in these different environments correctly, in a clean way.
In more detail: the actual definition you give is a "factory function": a function that returns the module contents when evaluated. A factory function is a very flexible thing that can be used in a variety of ways.
Browser globals
This is essentially your third example. Here, the factory function is executed immediately, and assigned to a global variable:
var MyModule = (function () {
// this is the factory function
})(); // execute immediately
The result is that other modules can reference this module by using the global variable - but this means you have to be careful to load all the modules in the correct order.
Asynchronous Module Definitions
Asynchronous Module Definition syntax is a pretty simple syntax, which provides a function called define() (spec here). This lets you describe modules by providing their dependencies and the factory function:
define('module-name', ['dep1', 'dep2'], function (dep1, dep2) {
...
});
So here, module-name is defined, but the factory function will only be executed when all the dependencies are loaded - this means that you can load the module definitions in any order, and the module loader is responsible for executing them all properly.
CommonJS
In CommonJS environments (such as Node.js, which runs in the command line or on a server), there is a global(-ish) object called module. Whatever you assign to module.exports is considered to be the value of the module.
If you want to use this with a factory function, it's pretty similar to the browser globals scenario, just that you assign it to module.exports:
module.exports = (function () {
// this is the factory function
})(); // execute immediately
Option (d): All of the above
It's possible to detect which module loaders are available by inspecting the environment (e.g. typeof define and typeof module).
The code block at the top detects which module loader is available and uses the factory function with AMD, CommonJS or browser globals, depending which is available.
While you could in theory do this inline in your code, separating it out to the top is nice and neat.
I am using the following closure pattern to modularise my code:
(function(root) {
// MODULE CODE HERE
if (typeof module !== 'undefined' && module.exports) { // CommonJS
/* var dependencies = require(...) */
module.exports = myModule;
} else if (typeof define !== 'undefined' && define.amd) { // AMD
/* var dependencies...; */
define([/* dependencies */], function(/* dependencies */) {
/* Assign closure level vars to respective arguments */
return myModule;
});
} else {
// Dependencies??
root.myModule = myModule;
}
})(this);
i.e., We use feature detection to support CommonJS modules (e.g., node.js), AMD or basic global namespace instantiation.
This works fine in node.js; I haven't tested the AMD pattern yet, as I'm still reading up on it (See Edit 2: AMD exhibits the exact same effect); but it fails in the browser if the module has any dependencies. That is, say, if myModule references something that is defined in a different module: For example, say if I had super.js and child.js with respective module definitions, as above, where super.js creates a function called root.super (root === window in the browser), if child.js tries to do super(), I will get something like super is not a function.
What's going on here?
To try to fix it, I changed the order in which super.js and child.js are loaded in <script> elements: No luck. Then I tried forcing child.js to load when the document is ready, using jQuery:
$(document).ready(function() {
$.getScript('child.js', function() {
// Do stuff with child, which calls super
});
});
...again, same problem. However, in both cases, if I enter the console, super is available and defined as I'm expecting.
Why is super in child.js presumably from a different (i.e., not the global) scope?
I should add, if I remove the dependency injection bit in the CommonJS export, it fails in node.js with the same error (if there are any dependants).
EDIT #Amberlamps' answer solved the problem, but it didn't answer the question as to why this occurs. My module pattern is now:
(function(root) {
// MODULE CODE HERE
if (typeof module !== 'undefined' && module.exports) { // CommonJS
/* var dependencies = require(...) */
module.exports = myModule;
} else if (typeof define !== 'undefined' && define.amd) { // AMD
/* var dependencies...; */
define([/* dependencies */], function(/* dependencies */) {
/* Assign closure level vars to respective arguments */
return myModule;
});
} else {
if (root.hasOwnProperty(/* dependencies */)) {
/* var dependencies = root... */
root.myModule = myModule;
}
}
})(this);
This keeps dependants with a common name, across environments. However, the question remains: Why is the global object not available within the closure's scope?
EDIT 2 I've been experimenting with RequireJS and AMD and have corrected my code, above, so that the AMDs work. Exactly the same thing happens in this case, too: You have to explicitly assign the global object to a variable within the closure for it to be available within said closure...
This pattern works just fine. If you actually tested it with a function called super and calling it via super(), you might ran into an error, because super is a reserved word. Following code works fine:
(function(root) {
root.super = function() {
console.log("hello");
};
}) (window);
(function(root) {
root.super();
}) (window);
You can call your function using window.super(). super() however will result in an error.