JavaScript Modules, Closures and Scope - javascript

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.

Related

Call of anonymous function (vanilla js, pure js, no jquery)

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.

Universal module definition - writing style

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.

Export module pattern

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.

How do I use a closure for each JS file, but still have the files interact with each other.

I understand that using closures (IIFE) is the best practice as it prevents polluting the global namespace. However, when I added the closures to my files, it prevented my 2nd file (controllers.js) from reading the first file (models.js). To give you an idea, here's what they look like:
models.js
;(function() {
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
})();
controllers.js
;(function() {
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
})();
Now that I've added closures on them, I'm getting an error that searchResult is undefined in controllers.js -- because it can't see that it exists in the models.js. How do I get it to understand that it exists in the other file?
P.S. Yes, models.js is added in the HTML file before the controllers.js file.
For them to interact, they have to have some common symbol. You have a couple of choices:
Do it yourself (using a single global variable)
Use some kind of library that does it for you (using [ideally] just a single global symbol)
Do it yourself a different way that requires no global common symbol at all
Do it yourself
The DIY version is, typically, that you have a single global, for your entire app, which your various modules add properties to.
So for instance:
models.js:
;(function(globals) {
var MyApp = globals.MyApp = globals.MyApp || {};
MyApp.searchResult = searchResult;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
})(this);
That works because in loose mode, this at global scope is the global object (window on browsers). We pass it into the IIFE as the argument globals, and then either use or create a property on it called MyApp, and add searchResult to it as a property.
controllers.js:
;(function(globals) {
var MyApp = globals.MyApp = globals.MyApp || {};
function storeSearchResults(jsonObj) {
var instance = new MyApp.searchResult(jsonObj.data[i]);
/* Do more */
}
})(this);
We do the same thing, except that controllers.js is expecting that models.js has already been run. Although we still do the var MyApp = globals.MyApp = globals.MyApp || {}; bit, the new MyApp.searchResult would of course fail if models.js hadn't been run.
There are probably a dozen syntactic variations on this theme, this is just one of them.
Use some kind of library that does it for you
Your other option is to use a library like RequireJS (the one global symbol there is require, and it's a function) or any other asynchronous module definition lib.
Do it yourself another way
Another DIY option gets rid of globals entirely, you don't even need a single global.
To do that, your individual files don't have the IIFE (although they can use ones for things they don't want to share with other files):
;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
controllers.js:
;
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
Then you use a minifier to combine your scripts and wrap them in one big IIFE. You might have pre.js:
(function() {
and post.js:
})();
Then the minifier creates app.js by combining pre.js + models.js + controllers.js + post.js. The end result (un-minified and formatted here for readability) is:
(function() {
;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
;
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
})();
I called this DIY, but I wouldn't be surprised if there were tools to help with it.
Events and listeners passing and receiving data.
I don't use globals.
However I don't know how to use events in raw javascript or if data passing can be done. I use jquery/node which allow for passing data and just works beautifully.

Node.js, Mocha, make globals in closures available

I am currently setting up some mocha tests using Node and in general they work. I now came across a problem I am not able to resolve.
I have a JS file containing the following: MyClass.js
(General CoffeeScript output for class MyClass + constructor: ->)
EDIT: This is browser code, I just want to use Node to test it. (Is that even desirable?)
(function() {
window.MyClass = (function() {
function MyClass() {
// Do something cool here
}
return MyClass;
})();
}).call(this);
I now require MyClass.js in my test file. Once I run it, it directly throws an error
Testfile:
var myclass = require('MyClass.js');
...
describe('MyClass', function() { ... });
Error:
ReferenceError: window is not defined.
So far, I understand why this is happening, window does not exist in Node. But I cannot come up with a solution. I actually do not need the real window object specifically, so I thought mocking it would be enough. But it is not...
var window = {},
myclass = require('myclass.js');
...
describe('MyClass', function() { ... });
This command is also not helping: $ mocha --globals window
I still end up with the same error.
Any idea is much appreciated!
You don't actually want the window object, what you want is the global object. Here is some code that can get it in the browser (in which case it will be the same as 'window') or in node (in which case it will be the same as 'global').
var global = Function('return this')();
Then set things on that rather than on 'window'.
Note: there are other ways of getting the global object, but this has the benefit that it will work inside strict mode code too.
With following code you can use your class-like object in web-browser environment and Node.js without modification. (Sorry, I don't know how to translate that to CoffeeScript)
(function (exports) {
var MyClass = (function() {
function MyClass() {
// Do something cool here
}
return MyClass;
})();
exports(MyClass);
})(function (exported) {
if (typeof module !== 'undefined' && module.exports) {
module.exports = exported;
} else if (typeof window !== 'undefined') {
window.MyClass = exported;
} else {
throw new Error('unknown environment');
}
});
As you already have a scope which doesn't pollute global name-space, you could reduce it to:
(function (exports) {
function MyClass() {
// Do something cool here
}
exports(MyClass);
})(function (exported) {
// see above
});
I'm not an expert in AMD, require.js and other module loaders, but I think it should be easy to extend this pattern to support other environments as well.
Edit
In a comment you said that the above solution is not working when translated back to CoffeeScript. Therefore, I suggest another solution. I haven't tried it but maybe this could be a way to solve your problem:
global.window = {}; // <-- should be visible in your myclass.js
require('myclass.js');
var MyClass = global.window.MyClass;
describe('MyClass', function() {
var my = new MyClass();
...
});
It's a horrible piece of code, but if it works, maybe for testing purposes it's sufficient.
Due to the module loading behaviour of node.js this only works if your require('myclass.js') is the first require of this file in the node process. But in case of testing with Mocha this should be true.
1) What you are looking for is the module.exports to expose things in Node:
http://openmymind.net/2012/2/3/Node-Require-and-Exports/
2) Also you don't need IIFE in Node, you can drop the (function() {...
3) You can alway look at some popular Node repo on Github to see examples, look at the Mocha code since you're using it, you'll learn a thing or two.
Something like jsdom is lighter than PhantomJS and yet provides quite a few things you need to test code that expects to be running with a proper window. I've used it with great success to test code that navigates up and down the DOM tree.
You ask:
This is browser code, I just want to use Node to test it. (Is that even desirable?)
It is very desirable. There's a point at which a solution like jsdom won't cut it but as long as your code is within the limit of what jsdom handles, might as well use it and keep the cost of launching a test environment to the minimum needed.
#hgoebl: As I'm not the OP, I can not add his original CoffeeScript code, but here is my example:
pubsub.coffee:
window.PubSub = window.PubSub || {}
PubSub.subscribe = ( subject, callback )->
now the test:
assert = require "assert"
pubsub = require './pubsub.coffee'
describe "pubsub.http interface", ->
it "should perform a http request", ->
PubSub.subscribe 1, 2
what works for me up to now is:
window.PubSub = window.PubSub || {}
window.PubSub.subscribe = ( subject, callback )->
and the test:
`window = {}`
assert = require "assert"
pubsub = require './pubsub.coffee'
describe "pubsub.http interface", ->
it "should perform a http request", ->
window.PubSub.subscribe 1, 2
The main drawback of the solution, is that I have to explicitly mention the window object in the implementation and the test. User code executed in a browser should be able to omit it.
I now came up with an other solution:
window = window || exports
window.PubSub = window.PubSub || {}
PubSub = PubSub || window.PubSub
PubSub.subscribe = ( subject, callback )->
and then in the test, simply requiring the PubSub namespace:
PubSub = require( './pubsub.coffee' ).PubSub
And finally, the solution from kybernetikos applied looks like this:
global = `Function('return this')()`
global.PubSub = global.PubSub || {}
PubSub.subscribe = ( subject, callback )->
As now, the PubSub namespace is in the global namespace, just a simple require is needed in the file that contains the mocha tests:
require( './pubsub.coffee' )

Categories

Resources