Optimizing JavaScript - Java Script

When creating desktop applications, most developers don’t need to think much about optimization.For the most part, programming languages are optimized when they are compiled: All variables, functions, objects, and so on, are replaced with symbolic pointers that are understood only by the processor.

Macros are compiled to be faster than function calls. Templates are used to speed up object creation. But JavaScript is a very different animal because it’s downloaded as source code and then interpreted (not compiled) by the browser. Because of this, the speed of JavaScript code is split into two categories: download time and speed of execution.

Download time

When using Java or other such programming languages, developers need not give any thought to having variable names that are 100 characters long because the names are all replaced; they need not worry about writing paragraphs of comments because these, too, are removed. As a JavaScript developer, you do not have this luxury.

Web browsers download JavaScript as source code, meaning that all long variable names and comments are included. These and other factors increase the download time and thus increase the overall time it takes for the script to run. The key factor in decreasing download time is the number of bytes that a script contains.

The key number to remember is 1160, which is the number of bytes that fit into a single TCP-IP packet.It’s best to try to keep each JavaScript file to 1160 bytes or less for optimal download time.

Every character in a JavaScript file is a byte. Thus, every extra character (whether it be a variable name, function name, or comment) counts against the download speed. Before deploying any JavaScript code,the download time should be optimized as much as possible.Here are a handful of ways that you can decrease the overall number of bytes in a script.

Remove all comments

This should be a no-brainer, but many developers forget this because,once again,compilers have traditionally handled this.
Any comments in a script should be removed prior to deployment.Comments are important while you are developing so that all team members can understand the source code. However, when it comes time for deployment, those comments are slowing down your JavaScript code dramatically.

Removing comments is the easiest way to cut down the number of bytes in a JavaScript file. Even if you don’t follow any of the other suggestions I give, this alone can provide dramatic decreases in overall file size.

Remove tabs and spaces

Most good developers indent their code regularly in order to increase readability. This is good practice, but the browser doesn’t need all those extra tabs and spaces; they must go. And don’t forget about the spaces between function arguments, assignments, and comparisons: say goodbye to those as well.Consider the following two lines:

function doSomething ( arg1, arg2, arg3 ) { alert(arg1 + arg2 + arg3); } function doSomething(arg1,arg2,arg3){alert(arg1+arg2+arg3);}

To a JavaScript interpreter, these two lines are exactly identical, although the first line contains 12 more bytes than the second. Removing the extra tabs and spaces between arguments, parentheses, and other language delimiters helps to decrease the overall file size and, in turn, decreases the download time.

Remove all line breaks

The next most important (and simple)thing you can do to decrease the size of the script file is to remove all line breaks. As long as you program appropriately by including a semicolon at the end of each line,the line breaks are of no consequence.

Many schools of thought exist about line breaks in JavaScript,but the bottom line is that line breaks increase the readability of your code to prying eyes. Removing them is a fast, easy way to make it more difficult for anyone to reverse engineer your code.

Replace variable names

This is the toughest optimization method to implement. Replacing variable names usually can’t be done by hand because the process is not a standard find-and-replace text operation.

The basic idea is that any variable name (or private property of an object) should be replaced with a nonsense variable name that has no intuitive meaning when read in the code. After all,the name of the variable doesn’t matter to the interpreter, only to the developer who wants his or her code to make sense. When it comes time to deploy the script, however, eliminate those descriptive variable names with simpler,shorter ones:

function doSomething(sName,sAge,sCity){alert(sName+sAge+sCity);} function doSomething(a1,a2,a3){alert(a1+a2+a3);}

The first line of code above is the original; the second line has the argument names replaced.By doing this,the byte count was reduced by 16. Just imagine the savings if all the variable names in your entire script are replaced with two-character names.

The ECMAScript cruncher

Following the four steps listed previously can be difficult.To help with the process, you can use an external program.

One of the best tools for JavaScript code minimizing and variable replacement is the ECMAScript Cruncher (ESC) by Thomas Loo. ESC is a small Windows Shell Script that can do all the optimizations mentioned in this section for you.

To run ESC, you must be using a Windows. Open up a console window and use the following format:

cscript ESC.wsf -l [0-4] -ow outputfile.js inputfile1.js [inputfile2.js]

The first part, cscript, is the Windows Shell Script interpreter.The filename ESC.wsf is the ESC program itself. After that is the crunch level, which is a number between 0 and 4,indicating which optimizations should be applied. The –ow option indicates that the next argument is the output file for the optimization.Finally, all remaining arguments are the JavaScript files to optimize.You can specify only one fileto optimize or multiple files (which are optimized and placed into the output file one after the other).

The four levels of optimization supported by ESC are explained in the following table:

The ECMAScript cruncher

ESC is very good at replacing variable names with nonsense names. It also is intelligent enough to replace private object properties and methods (those denoted with two underscores at the beginning and end of the name) with nonsense names,so your private properties and methods stay private.

ESC leaves your constructor names, public properties, and public methods intact, so no need to worry about those. The only thing to keep in mind when using ESC is that if the JavaScript file references a constructor in another file (such as the StringBuffer class from earlier in the book),crunching at level 4 replaces the references to this constructor with a nonsense name.The solution is to crunch both files into a single file, thus retaining the constructor name.

Other ways to decrease the byte count

The following are suggestions to optimize scripts for size using somewhat uncommon methods that save a lot of bytes. These suggestions aren’t handled by ESC, so you must carry them out by hand.

Replace Boolean values

You learned early on that for comparison purposes, true is equal to 1 and false is equal to 0. Therefore, anytime a script contains the literal value true, it can be replaced with a 1 and anytime a script contains false, it can be replaced with a 0. This saves three bytes in the case of a true and four bytes in the case ofa false, but it doesn’t alter the meaning of any Boolean expression.
Consider this example:

You can replace the true and false without changing the meaning:

This code runs exactly the same way, and you’ve gained seven bytes.

Shorten negative tests

It’s quite common to test whether a value is valid. The most these negative tests can do is determine if a variable is equal (or not equal) to undefined, null, or false, such as in the following:

These are all fine, but they can all be rewritten using the logical NOT operator and have the exact same effect:

How is this possible? Remember way back at the beginning of the book when automatic type conversions were discussed? The NOT operator returns true when its operand is undefined, null, or false (it also returns true if the operand is 0). Take a look at the byte savings anytime you replace one of these negative tests with the logical NOT operator.

Use array and object literals

The concept of array literals was touched on briefly earlier in the book. To review, the following two lines are equivalent:

The second line uses an array literal, which is just as valid as the first line. But as is very apparent, the second line uses fewer characters (and thus bytes). It’s good practice to always use array literals when creating arrays.

Likewise, object literals can be used to save a lot of space as well. The following two lines are equivalent, but the one using an object literal uses fewer bytes:

This also works if you are creating a generic object with a few properties, such as this:

The previous code can be rewritten using an object literal as the following:

This example uses an object literal to assign the two properties color and name, and in doing so, saves a lot of bytes.

Execution time

The second part of overall JavaScript performance is the amount of time it takes a script to run.Because JavaScript is an interpreted language, the speed of execution is significantly slower than compiled languages.

Geoffrery Fox of Syracuse University wrote an online seminar entitled “JavaScript Performance Issues”, in which he described JavaScript’s performance relative to other well-known programming languages. According to Fox, JavaScript is:

  • 5000 times slower than compiled C
  • 100 times slower than interpreted Java
  • 10 times slower than interpreted Perl

Keeping this in mind, you can do some simple things to improve the performance of your JavaScript code.

Be scope aware

In JavaScript, scope is everything. A scope can be thought of as the space where certain variables exist. The default (or global) scope is the window in JavaScript. Variables created in the window scope aren’t destroyed until the Web page is unloaded from the browser. Each function you define is another scope under that global scope. All variables created within the function exist only within the function scope and are destroyed when execution leaves the function.

You can think of scopes in JavaScript as a hierarchical tree. When a variable is referenced, the JavaScript interpreter looks to the most recent scope to see if it exists there. If not,it goes up to the next scope, and the next, and so on, until it reaches the window scope. If the variable is not found in the window scope, you receive an error during execution.

Every time the interpreter goes up to another scope in search of a variable,execution speed suffers. Variables that are local to a scope lead to faster script execution when compared to global variables. The less distance the interpreter has to travel up the tree,the faster your script runs. But what exactly does this mean? Consider the following example:

When fn3() is called on the last line, a scope tree is created (see Figure).

The importance of understanding scopes becomes evident in this example as you travel down the scope tree. The function fn3() calls fn2(), which calls fn1(). The function fn1() then accesses the windowlevel variable sMyFirstName, but in order to locate this variable, the interpreter has to look back up the scope tree all the way to the window scope. This takes significant time. Finding ways to take advantage of JavaScript scoping is vital in optimizing execution time. You can do a few easy things to help the interpreter locate variables faster.

scope tree is created

Use local variables

Always use the var statement to define variables inside functions. Whenever you used var, a local variable is created within the current scope. If you begin using a variable without first defining it with var, the variable is created at the window scope, meaning that every time you use that variable the interpreter has to search back up the scope tree to find it.For example, don’t do this:

In this function, the variable sMyFirstName is assigned a value without using var; this variable is created at the window scope. You can prove this to yourself by defining another function that also uses this variable:

In this example, you see two alerts displaying “Nicholas”. This happens because the first function call, sayFirstName(), creates the variable sMyFirstName at the window scope, which means the second function, sayFirstNameToo(), can also access it. Suppose you now change this example to use var, like this:

When you try to run this code,you get an error after the first alert is displayed because the second function has no knowledge of a variable named sMyFirstName. The variable was created within the sayFirstName() scope and was destroyed when the function finished executing.

Using local variables leads to faster execution because the interpreter doesn’t have to leave the local scope in search of a variable. Local variables are also much more efficient because they are removed from memory long before the Web page is unloaded.

Avoid the with statement

You probably understand at this point that the fewer scopes you have, the better off you are. This is why it’s important to avoid the with statement whenever possible.

As a quick refresher, the with statement enables you to access properties of objects as if they were variables, so instead of doing this:

You can do this:

This saves bytes by eliminating the need to type document for each of the three lines contained in the with statement. But the with statement comes with a price: It is another scope. When you use the with statement, you are forcing the interpreter to not only look up the scope tree for local variables, but you are also forcing it to test each variable against the object specified to see if it’s a property. After all, youcould have a variable named title or location defined within the function as well.

Your best bet is to avoid using the with statement. The number of bytes saved doesn’t outweigh the performance loss because of an added scope.

Remember Computer Science 101

A lot of the basics relating to optimization of code in other programming languages also apply to JavaScript. Because JavaScript borrows syntax and statements so heavily from C, Java, and Perl, the same techniques used to optimize code in those languages can also be used in JavaScript. The techniques presented in this section have been written about in books and articles such as Koushik Ghosh’s “Writing Efficient C and C Code Optimization” cpp/C___Code_Optimization.asp). In this article, Ghosh has compiled a list of the most popular code optimization techniques for C, many of which apply to JavaScript as well.

Choosing the right algorithm

When programming, choosing the right algorithm is as important as anything you do.The less complex the algorithm, the faster your code runs. To measure the complexity of algorithms, computer scientists use Big O notation. Big O notation consists of the letter O defined as a function with certain arguments.

The simplest algorithm is a constant value, represented as O(1). Retrieving a constant value is an extremely fast process. Constant values are made up of both true constants, such as the number 5, as well as values stored in variables. Consider the following example:

This code retrieves three constant values. The first two are the number 10 and the variable iFive in the second line. Then, in Line 3, the value of iSum is retrieved; this is also a constant value. These three constant retrievals take very little time because of their simplicity.

All values stored in arrays are also constant values, so the following code uses only the constant value algorithm:

In this code the array aNumbers is used to store the numbers to be added. Both aNumbers[0] and aNumbers[1] are constant value retrievals, so the total number of O(1) algorithms in this code is also three.

The next algorithm is called linear, represented by O(n).This algorithm is used in simple searches, where an array is searched through, item by item,until a result is found. The following is an example of a linear algorithm:

This algorithm is fairly common because iterating over the values in an array is a very common technique. However, another linear algorithm is used commonly in JavaScript: the named property lookup.

Named properties are just basic properties of objects, such as oDog.name. Although this may look like a variable that is local to the given object, in reality, it’s actually a search through the properties of the object looking for the one matching name. For this reason, it’s always best to use local variables or numerically indexed array values instead of named properties.

Consider this example:

Here are the algorithms represented in this code:

  • i in i < aValues.length — constant(O(1))
  • aValues.length in i < aValues.length — linear(O(n))
  • i in alert(i + “/” + aValues.length + “=” + aValues[i]); — constant(O(1))
  • aValues.length in alert(i + “/” + aValues.length + “=” + aValues[i]);—linear(O(n))
  • aValues[i] in alert(i + “/” + aValues.length + “=” + aValues[i]);—constant(O(1))

The linear algorithms are of particular interest here because they can easily be replaced with constant algorithms. All the linear algorithms are run twice every time the loop runs: once when the i < aValues.length test is run after each loop iteration, and once in the alert() call.That means the linear algorithms are executed 16 times throughout the function execution.

The following code does the exact same thing, but uses only one linear algorithm:

In this example, a second variable is initialized in the for loop, iCount, which is assigned the value of aValues.length. Then, the test after each iteration of the loop becomes a constant algorithm because it is accessing the variables i and iCount instead of aValues.length. Likewise, by replacing aValues.length with iCount in the alert() call, another linear algorithm is eliminated. Ultimately, this code executes with only one linear algorithm instead of 16, which decreases execution time.

Keep these algorithms in mind when trying to optimize your code. Here are a couple basic rules for using the correct algorithms:

  • Whenever possible,use local variables or numerically indexed arrays instead of named properties.
  • If a named property is going to be used more than once, store its value in a local variable to avoid running a linear algorithm each time the value is needed.

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

Java Script Topics