IIFE inside $(document).ready or the other way around - javascript

My colleague has been extensively using IIFE inside (document).ready in his code. Now, I've read this post:
JQuery best practise, using $(document).ready inside an IIFE?
This got me thinking if we should use $(document).ready inside IIFE or is it also fine the other way around as my colleague is doing.
So basically, his code is setup like this:
jQuery(function() {
(function($) {
//...
// Code here
//...
})(jQuery);
});
Is what he is doing generally fine?

Some may argue that this is a matter of style/opinion, but if you consider the typical goal of an IIFE in that context I believe the answer is yes, it is acceptable to use your alternative way, but there is a potential drawback.
Wikipedia has to say that:
Immediately-invoked function expressions can be used to avoid variable hoisting from within blocks, protect against polluting the global environment and simultaneously allow public access to methods while retaining privacy for variables defined within the function.
Neither method pollutes the global namespace, because they don't declare any variables. Therefore, it should be fine to use either way. Although note that it is partially redundant because the function handler for the ready event already creates a new scope, and also note it is the most common practice to see IIFE functions encapsulate all of the code in a file.
One drawback to the way that your colleague is using: If you did want to do some javascript logic that didn't depend on the DOM being ready, then you would not have the benefits of the IIFE if you put your code outside of the IIFE. So something such as this would not be safe:
// Non-DOM-ready-required code here (NOT scope-safe)
jQuery(function() {
(function($) {
//...
// DOM-ready-required code here
//...
})(jQuery);
});
Using the common style gives you the full IIFE benefit:
(function($) {
// Non-DOM-ready-required code here (scope-safe)
$(function() {
//...
// DOM-ready-required code here
//...
});
})(jQuery);

In my opinion, your colleague is jumping through an extra hoop for no reason. I'm going to ignore the outer $(document).ready as it's of no real consequence.
/*jQuery(*/function() {
(function($) {
//...
// Code here
//...
})(jQuery);
}/*);*/
Using an IIFE as the entire body of the callback to jQuery confers no extra benefit in scope isolation. It's the same as if the code were
/*jQuery(*/function() {
//...
// Code here
//...
});
The only thing that the IIFE has done is allow you to reference jQuery as $.
Now, if there were more substantive code inside the callback, then there could be some benefit in using an inner IIFE. In the following example, the callback contains two IIFEs,
jQuery(function() {
// IIFE
(function($) {
//...
// Code here
//...
})(jQuery);
// IIFE 2
(function($) {
//...
// Code here
//...
})(jQuery);
});
and the use of IIFE's allows scope isolation between the two blocks of code.
Back to the main question:
This got me thinking if we should use $(document).ready inside IIFE or
is it also fine the other way around as my colleague is doing.
Now, to be honest, I don't understand why either of these should be regarded as a best practice, even after looking at your link. The people who answered never explained why it should be a best practice.
Looking at
(function($) {
$(document).ready(function() {
// other code here
});
})(jQuery);
versus
//(function($) {
$(document).ready(function() {
// other code here
});
//})(jQuery);
There really isn't any significant advantage. Just as in my earlier example, the use of the IIFE offers no scope isolation advantage, and does nothing other than to allow you to reference jQuery via $.
However, in the actual linked question , the code looks like this:
(function($) {
// other code here 1
$(document).ready(function() {
// other code here 2
});
})(jQuery);
and in this case, the IIFE does serve a purpose of keeping the variables used in other code here 1 from leaking out to global scope. But this has nothing at all to do with $(document).ready(). Restructuring the code as
(function($) {
// other code here 1
})(jQuery);
jQuery(document).ready(function() {
// other code here 2
});
does the same thing.
The moral of this answer is that using an IIFE to wrap loose code that's not already inside a function gives you benefits, while wrapping code that's already inside a function doesn't give you anything (unless you count changing the reference).

Related

How can I make a function defined inside another function be in the window scope?

I am creating a wrapper for some arbitrary code (let's call it managed code). The managed code may include some functions that are defined in the window scope and are expected by other scripts on the page (horrible, 1997, practices, I know, but such is what I have to deal with), as global functions.
The purpose of the wrapper is to delay executing the wrapped code until jQuery is loaded. It looks like this:
(function () {
var once = true,
check = setInterval(function () {
if (window.$ && once) {
once = false; // setInterval can stack up if the UI freezes. Ensure this only gets called once.
executeBundle();
clearInterval(check);
console.log('Jquery loaded');
}
}, 100);
})()
// Wrapper proper
function executeBundle() {
// oodles of code of any origin
}
Now that the managed code is wrapped inside the executeBundle function, all functions/variables declared within it will be scoped to that function. This isn't a problem for the managed code itself, but for other scripts that load separately that may rely on global functions it provides.
I'd like to know if anyone knows a strategy like eval, but without the security issues, that may allow me to preserve the window scope for the running of the managed code. The constraint is that I can't modify the managed code at all--just the wrapper.
Based on T.J. Crowder's phenomenal answer, I realized that I could add the managed code to a <script> element and add that to the <head> like this:
var codeBundle = // Code in one long string
function evaluateBundle() {
var script = $('<script type="text/javascript"/>')
script.html(codeBundle);
$('head').append(script);
}
And let the parser evaluate the code.
I'd like to know if anyone knows a strategy like eval, but without the security issues
If you're evaling code of your own that you would run by having it in a script tag anyway, there are no security issues. You're running code either way.
You can't do this if the code you're wrapping will appear directly within evaluateBundle and it has declarations (vars and function declarations) that were supposed to be at global scope. Handling those would require modifying the wrapped code.
You can do this if you load that code separately, though, and then do a global eval on it. For instance, put it in a script block with a non-JavaScript type so the browser doesn't execute it:
<script type="x-code-to-wrap"></script>
...and then:
function evaluateBundle() {
var code = document.querySelector('script[type="x-code-to-wrap"]').textContent;
(0, eval)(code);
}
(The (0, eval)(code) bit is the global eval, more on MDN).
You may have to adjust the textContent part of that for cross-browser compatibility. This question's answers suggest using jQuery's html function:
function evaluateBundle() {
(0, eval)($('script[type="x-code-to-wrap"]').html());
}
Live example on JSBin

start javascript code with $(function, etc

I am studying Backbone and the todo example apps from http://todomvc.com/
I have noticed there are 3 severals ways of starting the code in the files:
$(function() {
// code here
});
$(function( $ ) {
// code here
});
(function() {
// code here
}());
I do not understand the differences and when I should use one over the other.
I also saw some people using this to start their code:
$(document).ready(function(){
// code here
});
From what I have seen, this is the full way of writing it right?
In a more general way, should I always include my javascript code into something like that in each files?
Thanks for your advice.
$(document).ready(function(){}) ensures that your code runs on DOM ready, so that you have access to the DOM. You can read more about this in jQuery's documentation.
$(function(){}) is just an alias to #1. Any code in here will wait for DOM ready (see the docs).
$(function($){}) is equivalent to #1 and #2, only you get a clean reference to jQuery in the local scope (see the note below). You can likewise pass in $ to the function in #1, and it'll do the same thing (create a local reference to jQuery).
(function(){}()) is just a self-executing-anonymous-function, used to create a new closure.
Please note that none of these are specific to Backbone. The first 3 are specific to jQuery, while #4 is just vanilla JavaScript.
Note: To understand what's going on in #3 above, remember that $ is an alias to jQuery. However, jQuery is not the only library that uses the $ variable. Since the $ might be overwritten by someone else, you want to ensure that within your scope, $ will always reference jQuery - hence the $ argument.
In the end, it basically boils down to the following 2 options:
If your JavaScript is loaded in the head, you have to wait for document ready, so use this:
jQuery(function($) {
// Your code goes here.
// Use the $ in peace...
});
If you load your JavaScript at the bottom of your document (before the closing body tag - which you should definitely be doing), then there's no need to wait for document ready (since the DOM is already constructed by the time the parser gets to your script), and a SEAF (A.K.A. IIFE) will suffice:
(function($) {
// Use the $ in peace...
}(jQuery));
P.S. For a good understanding of Closures and Scope, see JS101: A Brief Lesson on Scope.
I guess it makes sense to start out, by realizing that $ = jQuery. The purpose of which down below when reading about namespaces within anonymous functions will make more sense. But in essence, you can use either of them. One would use jQuery() instead of $() if they were using multiple libraries, and wanted the $ to be used by the other one.
$(document).ready(function(){
// Here we have jQuery(document) firing off the ready event
// which executes once the DOM has been created in
// order to ensure that elements you are trying to manipulate exist.
});
​$(function () {
// Short-hand version of $(document).ready(function () { });
});
More information on Document.ready()
Putting the $ within the parenthesis ensures the jQuery $ alias (you can be safe it always signifies jQuery this way).
$(function ($) { /* code here : $ always means jQuery now */ });
Lastly you have an IIFE (Immidiately-Invoked Function Expression)
- IIFE explanation
(function (myNameSpace, $) {
// This is an anonymous function - it is ran instantly
// Usually used for namespaces / etc
// This creates a scope/wrapper/closure around everything inside of it
}(window.myNameSpace, jQuery));
The $ at the top (with it's matching jQuery on the bottom) signify that the
$ (dollar sign) stands for jQuery within the scope of the namepsace.
This is done to ensure that other libraries do not collide with what the developer
intends/wants the $ to be used with.
(function (myNameSpace, $) {
// Now because of all of this scope/wrapper/closure awesome...
// you can create -INTERNAL- variables (sort of like Private variables from other langs)
// this variable cannot be accessed outside the namespace unless it is returned / exposed
var internalVariable = '123'; // Internal
// Even Internal functions!
function privateFunction () {
console.log('this is private!');
}
// --------------------------------------------------------
// Public -method- of nameSpace exposing a private variable
// Notice we're using the myNameSpace object we exposed at the top/bottom
myNameSpace.nameSpaceMethod = function () {
privateFunction(); // we can call the private function only inside of the namespace
return internalVariable; // now we could get this variable
};
}(window.myNameSpace, jQuery)); // notice these mirror the above arguments in the anon function
More information on anonymous functions
Now if we're outside of the namespace, we can see how these internal/public methods and variables are effected:
// This will come up undefined
alert(internalVariable);
// This will trigger a -method- of the myNameSpace namespace - and alert "123"
// Showcasing how we access a Public method - which itself has access to the internal variable
// and exposes it to us!
alert(myNameSpace.nameSpaceMethod());
​
These two:
$(function() {
// code here
});
$(document).ready(function(){
// code here
});
Are directly equivalent, they are both the way to start some jQuery when the document has loaded. The former is just a shorter version of the latter.
This one:
(function() {
// code here
}());
is just a scoped function with zero parameters, which is immediately called with zero parameters.

Can a nested function be placed in the global scope of Javascript?

I have a situation where I'm trying to consolidate several Javascript files into one, and conditionally apply them. I don't want to modify the actual files. Is there a way I can wrap these files and call them on demand?
The catch is, some of these files have function xyz() {} in them. So, wrapping them with if (false) { function xyz() {} } makes bad things happen.
For example, if this is my code...
if (includeFile) {
/* The file */
function foo() {}
/* The file */
}
The problem becomes that Chrome will see foo() and place it in the global scope even if includeFile is false.
The easy solution would be to modify it to be var foo = function() {} but I can't modify these files.
I also have a general concern about running eval() on these functions since they are fairly huge. (Think jQuery wrapped in a function. If this isn't a problem then maybe eval is the answer?)
I was hoping I could nest functions and pass window in as the scope, but tried it on jsFiddle and it didn't seem to work.
(function() {
function foo() {
alert('it works');
}
}).apply(window, []);
foo();
There are a few similar questions. But, none addressed the same situation that I have. Thanks.
Have you considered using a library like require.js? There are several libraries out there that can do this for you in a more elegant fashion.
If you don't want a dependency-loading library, but you're using jQuery or some similar library, you can load scripts conditionally using an AJAX request (in jQuery, you'd use the script dataType). This is far better than a simple eval(), but less robust when you're trying to manage a series of dependencies.
Another option here, if you want to simply concatenate everything, is to wrap each file in an anonymous function and then assign the necessary elements to the window object, placing them in the global scope:
(function(window) {
/* file 1 here */
function xyz() {
// etc ...
}
/* end file 1 */
// now selectively choose what you want to be
// in the global scope
window.xyz = xyz;
}(window));
xyz();
This requires more work for you, however, to identify what you want to be available globally. Note that it's note necessary to pass window in as an argument to the anonymous function, but it's not a bad idea if you're going to be be referring to it multiple times (this speeds up references and allows for variable-name munging during code compression).

Is window.onload considered a global variable can I put it in my module pattern?

I'm refactoring about 600 lines of javascript into the module pattern. Here is a start from previous post:
I undertand the concept of anonymous methods...and sefl-executing....but not the scoping concepts...i.e. what global and window do.
window.onload=initialize_page;
(function (global) {
global['test'] = 'test';
function initialize_page()
{
/* fill here */
}
})(window);
Can I put window.onload=initialize_page into my module pattern? Or does it need to be put outside of it? Can someone explain how the access works?
EDIT 1: per Answer
(function () {
addEventListener('load', initialize_page);
function initialize_page()
{
alert ("hi");
}
})();
It is a global. Don't touch it like that, you'll overwrite any other code that tries to assign load handlers.
Use addEventListener (or attachEvent for old IE) instead. There are plenty of libraries that abstract the functionality.
I'm not an expert, but I think window.onload is a function that gets called, so you reset it to be another function (that you created). You're using it as a macro there. Since initialize_page does not exist when you passed it, it will probably get passed to window.onload as null.

Is this a smart way to organize code in jQuery?

for the longest time I was writing all my code inside like this ...
$(document).ready(function(){
$('.show_module').click(function(){
});
...
});
putting all sorts of click handlers in there. But recently I was introduced to a new way of doing things and I wanted to get a feel for if its a smart way of going about it.
the idea is to have all the handlers in related functions and then have minimal code in document.ready above.
So for example ...
$(document).ready(function(){
page_handler(); // other functions follow
});
function page_handler(){
$('.show_module').click(function(){
});
...
}
This seems to allow organization of related handlers in functions and use document.ready as more of an initializer.
I know in JavaScript functions and variables are 'hoisted' before code starts executing so
do_something();
function do_something(){
}
works for this reason, since the function is ready to use before do_something() is actually called, even though it appears before the actual function definition.
I was wondering if the same thing happens here and how 'good' this idea/way of doing things is.
That will expose all your handlers to the global (window) scope, which may lead to clashes.
I prefer to do this...
(function($) {
// Anything you do in here won't be attached to window.
var a;
// If you must have something global, set it explicitly
window.doSomething = function() { ... }
// We can also use $ for jQuery here, even if we have used jQuery.noConflict()
$('body')...
})(jQuery);
It depends on:
Do you want to reuse the functions?
- If yes, your structure is a good way to do it
How long is your code?
- If your code is not too long and not use anywhere else. I think you don't need to break it into functions.
I always put the functions before the document ready:
$(document).ready(function() { ... });
Which I would write in short-hand:
$(function() { ... });
Ideally it is better to have just one initialiser like above. This may be useful for you too if you want to write extensions/plugins in jQuery context.

Categories

Resources