Product Customization Best Practices

You are here:
< Back

ISOLATING VARIABLES

Properly isolate your script environment is a best practice to have so you don’t override global variables when it executes.

Immediately-invoked function expressions (IIFE) allow you do to that:

(function() {
    // your JS code
})();

Moreover, it makes your code flexible and robust so you can create global variables aliases as parameters for your script:

(function(window, $) {
    // your JS code
})(window, jQuery);

It’s all about scope

I bet you wouldn’t like your new script not to behave as you want because the gobgob variable you use is already declared somewhere else and you didn’t pay attention for that.

I would even be worse if your splendid slideshow plugin starts going wrong when your new script is executing. Why? Because you didn’t notice that this plugin use the same variable you do and that you just change its value – if so, it wouldn’t be such a splendid plugin by the way.

To avoid that kind of scenario, you have to isolate your script from the rest of the environment, so it’s still safe. It’s dead easy to do and, if your slideshow plugin were a good one, that’s what it should have done!

Few reminder about scope in JS

If you declare properly a variable, it would be accessible from the whole script which is inside it’s container.

var gobgob = "Hey!";
function myFonction() {
    console.log(gobgob);
}

// Outputs "Hey!"
myFonction();

On the contrary, a variable properly declared into a function is not accesible from the outside of this function: the scope is limited to the function.

However, if you forget to use the var keyword when you declare your variable, you’ll make it globalwhich means it would be accessible from anywhere – which is generally not a best practice.

function myFonction() {
    var gobgob = "Hey!";
    gubgub = "Ho!";
}

myFonction();

// Outputs "ReferenceError: gobgob is not defined"
console.log(gobgob);

// Outputs "Ho!"
console.log(gubgub);

We don’t want our script variables override any other variables which would eventually exists!

var gobgob = "Hey!";

// (...)
// Somewhere else far far away in your JavaScript...
// ... a wild override of an existing (and forgetted) variable appears!
var gobgob = "Ho!";

Don’t forget the love glove!

To resolve our problem, we just need to isolate variables so we limit their scope to the code we decided to.

To do so, we wrap our JavaScript code into what we called an IIFE – or a self-executing anonymous function apparently – :

(function() {
    // your JS code
})();

This code encapsulation is:

  • anonymous because you don’t name the function
  • self-executing or immediately-invoked thanks to the final little ();

Going back to our previous example:

var gobgob = "Hey!";

// We wrap our script properly
(function() {
    var gobgob = "Ho!";

    // Outputs "Ho!"
    console.log(gobgob);
})();

// Outputs "Hey!" -> bingo !
console.log(gobgob);

Nothing is changed for script execution.

But we don’t have to worry about other variables which could exist because we now work into a safe environment: we won’t modify a global variable inadvertently!

However, we can still use global variables inside our code:

var gobgob = "Hey!";

(function() {
    // Outputs "Hey!"
    console.log(gobgob);
})();

Rename global variables in our script

It’s even possible to pass variables as parameters for your script. You can generally redefine them for your script usage, which is good if you’re working with some library such as jQuery.

Imagine you’d like to create a tiny nice script using jQuery. You’d probably use $ everywhere.

What if I told you we’re now using another library which also uses $ as an alias? You’d probably pass jQuery in noConflict() mode… and you’ll have to change your script to replace $ with jQuery.

Or you’d have listen to me and these two lines of code would have properly isolate your script which would execute, regardless of your environment:

(function($) {
    // your JS script with a bunch of $
})(jQuery);

The good part of this technique is that you could quickly switch library without changing anything in your script, which is more flexible and stable:

(function($) {
    // your JS script with a bunch of $
})(Zepto);

Performance note – When you minify your script, it’d minify your variables’ name without ruining your code, so it would make it even smaller as a result.

// Example with minification for `window`
(function(a) {
    console.log(a === window); // Outputs `true`
})(window);

Tip 1: Create an alias for undefined

One of JS best practice that jQuery uses in its source code is to declare a last parameter which is not defined and would be equal to undefined in your script:

(function(window, $, undefined) {
    // Create a gobgob variable which value is `undefined`
    var gobgob;
    console.log(gobgob === undefined);  // Outputs `true`

    // Our last variable is an alias for `undefined`
})(window, jQuery);

Tip 2: Still access to variables, from the outside

Isolate your code is good. But you’d sometimes need to develop a plugin/a library and you’d like to access the main object from the outside, so you can use it.

There are a bunch of possibilities but here is, IMHO, the simplest one: you just need to attach your variable to window so it becomes global for the other scripts.

(function(window, undefined) {
    // Create a gobgob variable and attach it to window
    var gobgob = "Hey!";
    window.gobgob = gobgob;
})(window);

// Outputs "Hey!"
console.log(gobgob);

Overriding a JavaScript function while referencing the original

You have a function, a(), that you  want to override, but also have the original a() be performed in an order depending on the context. For example, sometimes when we are generating a page we’ll want to override like this:

function a() {
    new_code();
    original_a();
}

and sometimes like this:

function a() {
    original_a();
    other_new_code();
}

How do we get that original_a() from within the over-riding a()?

Well. you could do something like this:

var a = (function() {
    var original_a = a;

    if (condition) {
        return function() {
            new_code();
            original_a();
        }
    } else {
        return function() {
            original_a();
            other_new_code();
        }
    }
})();

Declaring original_a inside an anonymous function keeps it from cluttering the global namespace, but it’s available in the inner functions.

Be sure to include the () at the end. You want to call the outer function and store the result (one of the two inner functions) in a, not store the outer function itself in a.