WEBSITE

JavaScript Patterns : Essentials - Minimizing Globals

8/28/2012 8:33:36 PM

1. Writing Maintainable Code

Software bugs are costly to fix. And their cost increases over time, especially if the bugs creep into the publicly released product. It’s best if you can fix a bug right away, as soon you find it; this is when the problem your code solves is still fresh in your head. Otherwise you move on to other tasks and forget all about that particular code. Revisiting the code after some time has passed requires:

  • Time to relearn and understand the problem

  • Time to understand the code that is supposed to solve the problem

Another problem, specific to bigger projects or companies, is that the person who eventually fixes the bug is not the same person who created the bug (and also not the same person who found the bug). It’s therefore critical to reduce the time it takes to understand code, either written by yourself some time ago or written by another developer in the team. It’s critical to both the bottom line (business revenue) and the developer’s happiness, because we would all rather develop something new and exciting instead of spending hours and days maintaining old legacy code.

Another fact of life related to software development in general is that usually more time is spent reading code than writing it. In times when you’re focused and deep into a problem, you can sit down and in one afternoon create a considerable amount of code. The code will probably work then and there, but as the application matures, many other things happen that require your code to be reviewed, revised, and tweaked. For example:

  • Bugs are uncovered.

  • New features are added to the application.

  • The application needs to work in new environments (for example, new browsers appear on the market).

  • The code gets repurposed.

  • The code gets completely rewritten from scratch or ported to another architecture or even another language.

As a result of the changes, the few man-hours spent writing the code initially end up in man-weeks spent reading it. That’s why creating maintainable code is critical to the success of an application.

Maintainable code means code that:

  • Is readable

  • Is consistent

  • Is predictable

  • Looks as if it was written by the same person

  • Is documented

2. Minimizing Globals

JavaScript uses functions to manage scope. A variable declared inside of a function is local to that function and not available outside the function. On the other hand, global variables are those declared outside of any function or simply used without being declared.

Every JavaScript environment has a global object accessible when you use this outside of any function. Every global variable you create becomes a property of the global object. In browsers, for convenience, there is an additional property of the global object called window that (usually) points to the global object itself. The following code snippet shows how to create and access a global variable in a browser environment:

myglobal = "hello"; // antipattern
console.log(myglobal); // "hello"
console.log(window.myglobal); // "hello"
console.log(window["myglobal"]); // "hello"
console.log(this.myglobal); // "hello"

2.1. The Problem with Globals

The problem with global variables is that they are shared among all the code in your JavaScript application or web page. They live in the same global namespace and there is always a chance of naming collisions—when two separate parts of an application define global variables with the same name but with different purposes.

It’s also common for web pages to include code not written by the developers of the page, for example:

  • A third-party JavaScript library

  • Scripts from an advertising partner

  • Code from a third-party user tracking and analytics script

  • Different kinds of widgets, badges, and buttons

Let’s say that one of the third-party scripts defines a global variable, called, for example, result. Then later in one of your functions you define another global variable called result. The outcome of that is the last result variable overwrites the previous ones, and the third-party script may just stop working.

Therefore it’s important to be a good neighbor to the other scripts that may be in the same page and use as few global variables as possible. 

It is surprisingly easy to create globals involuntarily because of two JavaScript features. First, you can use variables without even declaring them. And second, JavaScript has the notion of implied globals, meaning that any variable you don’t declare becomes a property of the global object (and is accessible just like a properly declared global variable). Consider the following example:

function sum(x, y) {
    // antipattern: implied global
    result = x + y;
    return result;
}

In this code, result is used without being declared. The code works fine, but after calling the function you end up with one more variable result in the global namespace that can be a source of problems.

The rule of thumb is to always declare variables with var, as demonstrated in the improved version of the sum() function:

function sum(x, y) {
    var result = x + y;
    return result;
}

Another antipattern that creates implied globals is to chain assignments as part of a var declaration. In the following snippet, a is local but b becomes global, which is probably not what you meant to do:

// antipattern, do not use
function foo() {
    var a = b = 0;

    // ...
}

If you’re wondering why that happens, it’s because of the right-to-left evaluation. First, the expression b = 0 is evaluated and in this case b is not declared. The return value of this expression is 0, and it’s assigned to the new local variable declared with var a. In other words, it’s as if you’ve typed:

var a = (b = 0);

If you’ve already declared the variables, chaining assignments is fine and doesn’t create unexpected globals. Example:

function foo() {
    var a, b;
    // ...
    a = b = 0; // both local
}


Note:

Yet another reason to avoid globals is portability. If you want your code to run in different environments (hosts), it’s dangerous to use globals because you can accidentally overwrite a host object that doesn’t exist in your original environment (so you thought the name was safe to use) but which does in some of the others.


2.2. Side Effects When Forgetting var

There’s one slight difference between implied globals and explicitly defined ones—the difference is in the ability to undefine these variables using the delete operator:

  • Globals created with var (those created in the program outside of any function) cannot be deleted.

  • Implied globals created without var (regardless if created inside functions) can be deleted.

This shows that implied globals are technically not real variables, but they are properties of the global object. Properties can be deleted with the delete operator whereas variables cannot:

// define three globals
var global_var = 1;
global_novar = 2; // antipattern
(function () {
    global_fromfunc = 3; // antipattern
}());

// attempt to delete
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true

// test the deletion
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"

In ES5 strict mode, assignments to undeclared variables (such as the two antipatterns in the preceding snippet) will throw an error.

2.3. Access to the Global Object

In the browsers, the global object is accessible from any part of the code via the window property (unless you’ve done something special and unexpected such as declaring a local variable named window). But in other environments this convenience property may be called something else (or even not available to the programmer). If you need to access the global object without hard-coding the identifier window, you can do the following from any level of nested function scope:

var global = (function () {
    return this;
}());

This way you can always get the global object, because inside functions that were invoked as functions (that is, not as constructors with new) this should always point to the global object. This is actually no longer the case in ECMAScript 5 in strict mode, so you have to adopt a different pattern when your code is in strict mode. For example, if you’re developing a library, you can wrap your library code in an immediate function and then from the global scope, pass a reference to this as a parameter to your immediate function.

2.4. Single var Pattern

Using a single var statement at the top of your functions is a useful pattern to adopt. It has the following benefits:

  • Provides a single place to look for all the local variables needed by the function

  • Prevents logical errors when a variable is used before it’s defined (see Section 2.2.5)

  • Helps you remember to declare variables and therefore minimize globals

  • Is less code (to type and to transfer over the wire)

The single var pattern looks like this:

function func() {
    var a = 1,
        b = 2,
        sum = a + b,
        myobject = {},
        i,
        j;

    // function body...

}

You use one var statement and declare multiple variables delimited by commas. It’s a good practice to also initialize the variable with an initial value at the time you declare it. This can prevent logical errors (all uninitialized and declared variables are initialized with the value undefined) and also improve the code readability. When you look at the code later, you can get an idea about the intended use of a variable based on its initial value—for example, was it supposed to be an object or an integer?

You can also do some actual work at the time of the declaration, like the case with sum = a + b in the preceding code. Another example is when working with DOM (Document Object Model) references. You can assign DOM references to local variables together with the single declaration, as the following code demonstrates:

function updateElement() {
    var el = document.getElementById("result"),
        style = el.style;

    // do something with el and style...

}

2.5. Hoisting: A Problem with Scattered vars

JavaScript enables you to have multiple var statements anywhere in a function, and they all act as if the variables were declared at the top of the function. This behavior is known as hoisting. This can lead to logical errors when you use a variable and then you declare it further in the function. For JavaScript, as long as a variable is in the same scope (same function), it’s considered declared, even when it’s used before the var declaration. Take a look at this example:

// antipattern
myname = "global"; // global variable
function func() {
    alert(myname); // "undefined"
    var myname = "local";
    alert(myname); // "local"
}
func();

In this example, you might expect that the first alert() will prompt “global” and the second will prompt “local.” It’s a reasonable expectation because, at the time of the first alert, myname was not declared and therefore the function should probably “see” the global myname. But that’s not how it works. The first alert will say “undefined” because myname is considered declared as a local variable to the function. (Although the declaration comes after.) All the variable declarations get hoisted to the top of the function. Therefore to avoid this type of confusion, it’s best to declare upfront all variables you intend to use.

The preceding code snippet will behave as if it were implemented like so:

myname = "global"; // global variable
function func() {
    var myname; // same as -> var myname = undefined;
    alert(myname); // "undefined"
    myname = "local";
    alert(myname); // "local"
}
func();


Note:

For completeness, let’s mention that actually at the implementation level things are a little more complex. There are two stages of the code handling, where variables, function declarations, and formal parameters are created at the first stage, which is the stage of parsing and entering the context. In the second stage, the stage of runtime code execution, function expressions and unqualified identifiers (undeclared variables) are created. But for practical purposes, we can adopt the concept of hoisting, which is actually not defined by ECMAScript standard but is commonly used to describe the behavior.

Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8
Visit movie_stars's profile on Pinterest.