Avoiding Multiple Copies of the IIFE Pattern - javascript

I have seen the following IIFE pattern (Does it have a name?), which according to this blog post protects against other plugins, improves minification and has a tiny performance benefit. I also personally added a namespace to the IIFE:
(function (MyNamespace, $, window, document, undefined) {
"use strict";
// My Code
})(window.MyNamespace = window.MyNamespace || {}, window.jQuery, window, document);
I have seen this pattern used in every JavaScript file. What about after concatenation and minification? How can I ensure that my code is wrapped with only one of the above IIFE's? Do tools like NPM, Gulp/Grunt help in this regard? Is there a standard way of handling this scenario?

After some investigation, library writers like jQuery and knockout handle this by pre-pending and appending the IFFE to the JavaScript. They then have a make file which concatenates everything together.
The start of the jQuery IIFE is a lot more complicated to handle non-browser scenarios.
jQuery intro.js
(function( global, factory ) {
if ( typeof module === "object" && typeof module.exports === "object" ) {
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
jQuery outro.js
return jQuery;
}));
Knockout (Thanks to #JamesThorpe) takes a slightly different approach to resolving the window and it's descendants.
Knockout extern-pre.js
(function(undefined){
var window = this || (0, eval)('this'),
document = window['document'],
navigator = window['navigator'],
jQueryInstance = window["jQuery"],
JSON = window["JSON"];
Knockout extern-post.js
}());
You can then use something like gulp-concat to sandwich the intro and outro files around your own script files.

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.

How jQuery is available outside the closure scope?

Sorry if this type of question asked many times before but I'm little unclear with the jQuery coding here:
From http://code.jquery.com/jquery-latest.js
(function(global,factory){
//some checks and coding
})(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
//coding stuff
})
So, the jQuery is using the coding in that closure but how the jQuery is available outside that scope?
In the very end of this IEFE closure you can see the lines
// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( typeof noGlobal === strundefined ) {
window.jQuery = window.$ = jQuery;
}
where they export the $ and jQuery identifiers to the global window object. This function with the // coding stuff itself is the second argument of the IEFE that you posted, and will - as the factory be called appropriately depending on what module loaders are available (or not).
Inside of the closure you can find this line of code:
return (window.jQuery = window.$ = jQuery);
So jQuery constructor function is explicetly assigned to window.jQuery. That's why it's available outside.

Why is this pattern needed?

Looking at the code of ryanve's response.js, it begins with the following:
(function(root, name, make) {
var $ = root['jQuery'] || root['Zepto'] || root['ender'] || root['elo'];
if (typeof module != 'undefined' && module['exports']) module['exports'] = make($);
else root[name] = make($);
}(this, 'Response', function($) {
...
What is this called, why is it useful and what does it do exactly?
This:
(function(root, name, make){...})(this,'Response', function($){...});
calls "init" function passing default namespace (this is window in browsers) reference as the root variable.
This:
var $ = root['jQuery'] || root['Zepto'] || root['ender'] || root['elo'];
gets reference of either jQuery or Zepto, etc. ... module.
And this:
if (typeof module != 'undefined' && module['exports'])
module['exports'] = make($);
else
root[name] = make($);
checks if file is loaded with module object (most probably CommonJS environment) available and calls "constructor" make() and registers its exhaust as a module.
Otherwise registers make() result as an object in global namespace ( root[name] = ... )
This is kind of hook. This is exactly you can add current plugin name "Response" with any of mentioned library like "jQuery, Zepto, ender, elo". And again "Response" leave anchor point
in following code.
if (typeof module != 'undefined' && module['exports']) module['exports'] = make($);
else root[name] = make($);
And code it self need some changes. Let us take jQuery, here "this" refers window, based on the code the author trying to like window.jQuery.Response = function(jQuery){} and module in "if" condition is a global. It will be available when you are working NodeJs like environment. In this case, Response attached with that global variable, instead of jQuery.
Note: Simply we can say, "Response" definition can work with any of mention library and/or library which expose "module" global.
This pattern helps avoid writing code in the global scope, and the function written on the first line of your code is a self invoking function. A self invoking function is invoked as soon as it is declared. Here is how to do it.
(function(argument1, argument2, argument2){
alert("Self invoked");
})(arg1, arg2, arg3);
in your case the first argument is this which refers to the window object, second is a string and third is a callback.

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' )

Advice to resolve window dependency when running in node.js

I'm trying to extend my browser-side library's runtime environment to node.js. I implemented the Universal Module Definition (UMD) pattern to do that. It works with AMD implementation and <script> but doesn't work in node.js since there is no window.
The dependencies in question are WebSocket, EventSource, XMLHttpRequest, document. The detail is described in here: https://github.com/flowersinthesand/portal/issues/115 So what is the best approach to resolve window dependency in node.js? I have though the following way to do that. Since I'm new to node.js, these are somewhat unfamiliar and I don't know which way is natural. Though I'm trying to support node.js, I don't want to change too much something.
The following code snippet comes from https://github.com/flowersinthesand/portal/blob/master/portal.js
Using jsdom
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(function() {
// Passes the window
return factory(root);
});
} else if (typeof exports === 'object') {
// Node
module.exports = factory(require('jsdom').something);
} else {
// Browser globals, Window
root.portal = factory(root);
}
}(this, function(window) {
If the jsdom supports the above dependencies well, this looks best. If it works, however, I wonder why client of sockjs, socket.io and engine.io don't use jsdom. Maybe, they are for node rather than browser? performance?
Making window as a plain object of dependency
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(function() {
// Passes the window
return factory(root);
});
} else if (typeof exports === 'object') {
// Node
module.exports = factory({
WebSocket: require('package name for WebSocket').something,
EventSource: require('package name for EventSource').something,
document: require('jsdom').hmm,
// ...
});
} else {
// Browser globals, Window
root.portal = factory(root);
}
}(this, function(window) {
It looks picky and somewhat uncomfortable to make a group of dependency, window, But, I want to preserve current code if possible. Also, jsdom may be required as well to resolve document used to do HTTP request by script tag when XMLHttpRequest can't work due to cross-domain or unloading event and this situation includes IE 6. It's possible to separate logic using document to use something supported in node and script tag in browser checking what the current runtime is, but it's big change for me.
(function(root, factory) {
if (typeof define === "function" && define.amd) {
// AMD
define(function() {
return factory(root);
});
} else if (typeof exports === "object") {
// Node
module.exports = factory(function() {
// Prepare the window powered by jsdom
var window = require("jsdom").jsdom().createWindow();
window.WebSocket = require("ws");
window.EventSource = require("eventsource");
return window;
}());
// node-XMLHttpRequest 1.x conforms XMLHttpRequest Level 1 but can perform a cross-domain request
module.exports.support.corsable = true;
} else {
// Browser globals, Window
root.portal = factory(root);
}
}(this, function(window) {

Categories

Resources