Javascript: Wrapping entire script in a function call - javascript

I've came across a few times with this phenomena in JavaScript, where an entire script is wrapped in a function call, like this:
(function() {
// statements...
})();
Real world example, from glow.mozilla.com client-side code:
https://github.com/potch/glow/blob/master/media/glow.js
What is this coding style used for? What's the difference between with and without the wrapped function style and when should it be used?

Doing it like this ensures that none of the variables/function you define go into the global scope. All scripts you include in the page share the same global scope, so if you define two variables in two separate scripts with the same name, they actually refer to the same variable.
For example, suppose you have a.js and b.js, defined like so:
// a.js
var node = document.getElementById("something");
function frob() {
node.style.display = "none";
}
// b.js
var node = document.getElementById("something-else");
If you include b.js after a.js in your page, then when you call frob it's going to hide the "something-else" node instead of the "something" node like you would expect.
Instead, you can do something like:
// a.js
(function() {
var node = document.getElementById("something");
window.frob = function() {
node.style.display = "none";
}
})();
// b.js
(function() {
var node = document.getElementById("something-else");
})();
And the stuff inside b.js isn't going to interfere with what's in a.js.
Note that in reality I wouldn't add functions directly onto window, instead I would do something similar to what that glow.js script you linked to has: a single global variable that represents my scripts "namespace". In jQuery, for instance, that single global variable is $ (or jQuery).

Related

Accessing a function defined inside a function from the global scope?

Long story short, I have a long code that uses jQuery. Lots of files, functions, etc. A less than ideal amount of our users are having issues with our code because some addons, toolbars and the like they have installed breaks our JavaScript code because of jQuery gets included twice and nasty stuff like that.
I thought I could just
Include jQuery
Use $.noConflict
Then include the whole rest of my code between something like:
.
(function($) {
// All of my code goes here.
})(jQuery);
I haven't checked if this fixes our issues with those users, but it does work. The problem is, in one part of the site (image upload) we have an iframe that needs to call some of those functions defined in our big chunk of code. I've tried putting those functions out of this unnamed function call, but it uses, on itself, other functions which have to be there.
Any idea or workaround of how could I be able to access functions defined inside that function (shown above) from a code that's outside of it?
Thanks!
You cannot access a function context from the "outside world". Well, to be accorate you could do it in some older js engines which allowed for accessing .__parent__ attributes, but that is old'n'busted and no longer available.
However, you would need to either expose some functions within your closure, or you creating a namespace object where you write all of your logic in (which also has to be available in the parent context).
So I'd suggest something like
(function( $ ) {
function myFunc() {
// do stuff
}
function anotherFunc() {
}
window.myFunc = myFunc; // expose myFunc globally
}( jQuery ));
Maybe even better:
var myNameSpace = { };
(function( $ ) {
myNameSpace.myFunc = function() {
// do stuff
};
}( jQuery ));
// somewhere else
myNameSpace.myFunc();
It is not an ideal practice, but you can declare those functions in the global scope.
(function($) {
globalFunct = function (arg1, arg2) { // Don't use var keyword
...
};
})(jQuery);
It isn't ideal because you can run into naming collisions, much like you are observing with jQuery. Improve upon this approach by putting all of your globally-accessible methods in a "package." Choose a unique name for it. This will prevent collisions.
// Somewhere outside of your anonymous function, in the global scope
var myPackage = {};
(function($) {
myPackage.globalFunct = function (arg1, arg2) {
...
};
})(jQuery);
Then call that method by invoking myPackage.globalFunct().
Why are you wrapping your code in a call to the jQuery function object which you pass in to your self-executing anonymous function; are you meaning to create a jQuery object from all of your code?
In order to expose your code to the outside world, you need to assign your functions and objects to an object which is outside of the scope of your code, such as the window object.
For example, if you had created an object containing various methods and properties that you wanted to expose, you could do this:
//Your self-executing anonymous function
(function($)
{
//Object which contains various useful methods and properties
var useful = {...};
//Expose it to the outside world
window.Useful = useful;
})(jQuery);
EDIT: as others have noted, it is not an ideal solution as you will indeed run into naming collisions if you are not careful. Also, using an object external to your anonymous function as a namespacing object (as others have stated) is my preferred method
Yes, you can "export" the function from within a closure:
Yes, you can "export" the function from within a closure:
(function() {
function a() {
console.log("a");
}
function b() {
a();
console.log("b");
}
// make b globally available
window.b = b;
})();
b();
window.PARTY_CATS_jQuery = jQuery.noConflict(true);
(function($) {
$(function() {
// All of my code goes here.
});
})(COMPANY_NAME_jQuery);
Then just use PARTY_CATS_jQuery in your global functions
If you feel PARTY_CATS_ is not a unique enough name pick something safer like BABY_KILLER_jQuery

Loading multiple scripts with same variables/functions

Is it possible to load multiple scripts with same variables/functions in JS, without overriding the old value of the variable. For example to create an own scope/sandbox or object for each loaded script.
Files to load:
script1:
<script>
function init() {
do something...
}
</script>
script2:
<script>
function init() {
do something...
}
</script>
And after loading call script1.init() or script2.init(), is this possible?
You could wrap each section of code with a self invoking anonymous function, which will effectively namespace that section.
(function() {
function init() {
// do something...
}
})();
init(); // ReferenceError
However, if you can't change the code, the second init will overwrite the first definition.
However...
And after loading call script1.init() or script2.init(), is this possible?
...is confusing. Do you already have the init() as methods of an object? If so, they won't overwrite each other.
Unfortunately, no. There is a single global scope on a single page.
However, by using something like the module pattern, you can make a fake namespace:
For example:
var script1 = (function() {
var that = {};
that.init = function() {
do something...
};
more functions...
return that;
})();
And then call it by using script1.init();
You can find out more about the module pattern here.

Enclosing external jQuery script

I have an external JavaScript file that will be used on pages with lots of other scripts. My script involves a lot of jQuery that listens for events, and by design, I have many global vars declared. I've been reading best practice articles, and a lot is said about 'polluting the global namespace' and inadvertent script interaction.
What's the best way to enclose (encapsulate?) my JavaScript file so that:
I can still access some of the
variables outside of the enclosure
The jQuery event listeners will
function properly
I'm not at liberty to disclose the code, so even general responses are appreciated. Additionally, any other tips on making scripts less vulnerable to other scripts on the page are welcome.
I've found enclosure styles for regular JavaScript, but does the use of jQuery complicate this?
Generally what this boils down to is encapsulating your objects into a "namespace". I use quotes there because the term is not an official semantic in JavaScript, but rather one that is achieved through basic object encapsulation.
There are several ways to do this, and it ultimately comes down to personal preference.
One approach is to just use a basic JS object, and keep everything in it. The name of the object should be semantic and give the object some meaning, but otherwise it's purpose is to just wrap your own code and keep it out of the global namespace.
var SomeName = {
alpha: 1,
beta: {a: 1, b: 2},
gamma: function(){
SomeName.alpha += 1;
}
}
In this case, only SomeName is in the global namespace. The one downside to this approach is that everything inside the namespace is public, and you have to use the full namespace to reference an object, instead of using 'this' - e.g. in SomeName.gamma we have to use SomeName.alpha to reference the contents of alpha.
Another approach is to make your namespace a function with properties. The nice feature of this approach is you can create 'private' variable through closures. It also gives you access to closured functions and variables without full namespace referencing.
var SomeName = (function(){
var self = this;
var privateVar = 1;
var privateFunc = function() { };
this.publicVar = 2;
this.publicFunc = function(){
console.log(privateVar);
console.log(this.publicVar); // if called via SomeName.publicFunc
setTimeout(function(){
console.log(self.publicVar);
console.log(privateVar);
}, 1000);
};
}();
The other bonus of this approach is it lets you protect the global variables you want to use. For example, if you use jQuery, AND another library that creates a $ variable, you can always insure you are referencing jQuery when using $ by this approach:
var SomeName = (function($){
console.log($('div'));
})(jQuery);
One method is to namespace like this:
var MyNamespace = {
doSomething: function() {},
reactToEvent: function() {},
counter: 0
}
You will just have to refer to the functions or variable using the namespace: MyNamespace.reactToEvent. This works fine for separating what you would normally have in the window (where all the confrontation is).
You can wrap your code in an anonymous Javascript function and only return what you want to expose to the outside world. You will need to prefix var to your global variables so that they remain only in the scope of the anonymous function. Something like this:
var myStuff = (function() {
var globalVar1;
var globalVar2;
var privateVar1;
function myFunction() {
...
}
function myPrivateFunction() {
...
}
return {
var1: globalVar1,
var2: globalVar2,
myFunction: myFunction
};
})();
Now you can access myStuff.var1 and myStuff.myFunction().
Two ways to encapsulate or limit namespace pollution
1) Create one global var and stuff everything you need into it.
var g = {};
g.somevar = "val";
g.someothervar = "val2";
g.method1 = function()
{
// muck with somevar
g.somevar = "something else";
};
2) For inline scripts, consider limiting the scope of the functions called.
<script>
(
function(window)
{
// do stuff with g.somevar
if(g.somevar=="secret base")
g.docrazystuff();
}
)(); // call function(window) then allow function(window) to be GC'd as it's out of scope now
</script>
I just started using RequireJS and have now become obsessed with it.
It's basically a dependency management system in a modular JavaScript format. By doing so you can virtually eliminate attaching anything to the global namespace.
What's nice is that you only reference one script on your page require.js then tell it what script to run first. From there it is all magic...
Here's an example implementation script:
require([
//dependencies
'lib/jquery-1.6.1'
], function($) {
//You'll get access to jQuery locally rather than globally via $
});
Read through the RequireJS API and see if this is right for you. I'm writing all my scripts like this now. It's great because at the top of each script you know exactly what you dependencies are similar to server-side languages - Java or C#.
This is a common practice with jQuery plugins for the same reasons you mention:
;(function ($) {
/* ... your code comes here ... */
})(jQuery);
This is an immediate function. If you declare your "global" variables inside, they will be local to this closure (still "global" for the code you create inside). Your event listeners will work inside here too, and you will still be able to reach real global variables.

Javascript question on functions

In my main Web Page (Viewer.aspx) I have a javascript script tag like this
<script language="javascript" type="text/javascript">
function initialize() {
var map = $find('Map1');
map.add_mouseMove(mouseMove);
}
</script>
Within those script tags I have a function. Is it possible to call another function that is in a different script tag like this?
<script language="javascript" type="text/javascript" src="Resources/JavaScript/proj4js-combined.js">
function mouseMove(sender,eventArgs) {
var source = new Proj4js.Proj('EPSG:3116');
var dest = new Proj4js.Proj('WGS84');
var p = new Proj4js.Point(px, py);
Proj4js.transform(source, dest, p);
}
</script>
Yes, this is done quite regularly as Javascript functions can be put into other files and pulled into pages that work this way.
Your second script tag specifies a src. The contents of the .js file will be loaded and parsed, but the code inside the script tag (the mouseMove function) will be ignored. If you want both the function and the contents of the .js file, you need to separate them into two different script tags.
As per your comment, here's what T.J. was talking about - you need to turn your second script block into something like this:
<script type="application/javascript" src="Resources/JavaScript/proj4js-combined.js"></script>
<script type="application/javascript">
function mouseMove(sender,eventArgs) {
var source = new Proj4js.Proj('EPSG:3116');
var dest = new Proj4js.Proj('WGS84');
var p = new Proj4js.Point(px, py);
Proj4js.transform(source, dest, p);
}
</script>
...but you should really just move the inline code block (what's inside of the 2nd <script> tag in my answer) to an external Javascript file.
Edit 1: What's the programming background you're coming from? If it's something like C# or Java, you'll need to forget what you know about those and approach Javascript completely differently. Javascript is an interpreted language, not a compiled one; among other things, this means that the order in which your functions are declared matters.
When I say "function declaration," I mean anything that looks like this:
function myNewFunction()
{
// anything else here
}
This tells the Javascript interpreter about a new function, called myNewFunction, whose body consists of whatever is in the curly braces.
Here's an example of what I'm talking about when I say you are using a function before you've declared it. Consider the following block of code (in isolation from any other Javascript, say, in an external Javascript file):
function foo() // this line declares the function called "foo"
{
}
function bar() // this line declares the function called "bar"
{
foo(); // this line invokes the previously declared function called "foo"
}
This will work as expected, because foo() was declared before you called it. However, what (it sounds like) you're trying to do is analogous to this:
function foo() // this line declares the function called "foo"
{
bar(); // this line tries to invoke the function called "bar"
// but it hasn't been declared yet! so it's undefined
}
function bar() // this line declares the function called "bar"
{
}
If you were to run this second Javascript snippet, it wouldn't work, because you're calling a function before it was declared.*
*Footnote: this is not actually the case, because using this syntax (function foo() { ... }) does something special and magical in Javascript. My particular example will actually work, but it illustrates the problem you're having.

Is it possible to destroy loaded JavaScript, including function & local variable?

I know. It is possible to dynamically load JavaScript and style sheet file into header of document. In the other hand, it is possible to remove script and style sheet tag from header of document. However, loaded JavaScript is still live in memory.
Is it possible to destroy loaded JavaScript from web browser memory? I think. It should be something like the following pseudo code.
// Scan all variables in loaded JavaScript file.
var loadedVariable = getLoadedVariable(JavaScriptFile);
for(var variable in loadedVariable)
{
variable = null;
}
// Do same thing with function.
Is it possible to create some JavaScript for doing like this?
Thanks,
PS. Now, you can use xLazyLoader and jQuery for dynamic loading content.
If the loaded script is assigned to a window property, for instance with the module pattern like so:
window.NiftyThing = (function() {
function doSomething() { ... }
return {
doSomething: doSomething
};
})();
or
window.NiftyThing = {
doSomething: function() { ... }
};
or
NiftyThing = {
doSomething: function() { ... }
};
Then you can delete the property that references it:
delete window.NiftyThing;
...which removes at least that one main reference to it; if there are other references to it, it may not get cleaned up.
If the var keyword has been used:
var NiftyThing = {
doSomething: function() { ... }
};
...then it's not a property and you can't use delete, so setting to undefined or null will break the reference:
NiftyThing = undefined;
You can hedge your bets:
NiftyThing = undefined;
try { delete NiftyThing; } catch (e) { }
In all cases, it's up to the JavaScript implementation to determine that there are no outstanding external references to the loaded script and clean up, but at least you're giving it the opportunity.
If, as Guffa says, the loaded script doesn't use the module pattern, then you need to apply these rules to all of its symbols. Which is yet another reason why the module pattern is a Good Thing(tm). ;-)
It might be possible to remove a Javascript file that has been loaded, but that doesn't undo what the code has done, i.e. the functions that was in the code are still defined.
You can remove a function definition by simply replacing it with something else:
myFunction = null;
This doesn't remove the identifier, but it's not a function any more.

Categories

Resources