Monthly Archives: July 2015

JavaScript Best Practicies : Performance Calculation

Test the speed of code with console.time

var firstRegimentNewbs = ["Grimble Horsehead", "Jark Winterborn", "Bunder Ropefist",
                          "Ernst Breadbaker"];
var firstRegimentKnights = [ *...tons of Knight objects...* ];
console.time("Time to add " + firstRegimentNewbs.length + " Knights");
for(var i = 0, x = firstRegimentNewbs.length; i

Multiple timers can run at one time.

var firstRegimentNewbs = ["Grimble Horsehead", "Jark Winterborn", "Bunder Ropefist",
                          "Ernst Breadbaker"];
var firstRegimentKnights = [ *...tons of Knight objects...* ];
var secondRegimentNewbs = ["Jenner Pond", "Tar Backstrand", "Cromer Treen", "Stim Lancetip",
                           "Vorn Sharpeye", "Rack Leaflets", "Bruck Valleyhome", "Arden Follower"];
var secondRegimentKnights = [ *...tons of Knight objects...* ];
console.time("Total completion time");
for(var i = 0, x = firstRegimentNewbs.length; i

Retrieving and using numerical time data
To accurately generate numerical data we can manipulate, we'll first examine the JavaScript Date object.
Placing a + unary operator in front of Date object variable asks for the specific value in milliseconds.

var rightNow = +new Date();

Implementation of speed test class

function SpeedTestClass(testImplementation, testParams, repetitions) {
  this.testImplementation = testImplementation;
  this.testParams = testParams;
  this.repetitions = repetitions || 10000;
  this.average = 0;
}
SpeedTestClass.prototype = {
  startTest: function() {
    var beginTime, endTime, sumTimes = 0;
    for (var i = 0, x = this.repetitions; i < x; i++){
      beginTime = +new Date();
      this.testImplement( this.testParams );
      endTime = +new Date();
      sumTimes += endTime - beginTime;
    }
    this.average = sumTimes / this.repetitions;
    return console.log("Average execution across " + this.repetitions + ": " + this.average);
  }
}

How to use created class:

var firstRegimentNewbs = ["Grimble Horsehead", "Jark Winterborn",
                          "Bunder Ropefist", "Ernst Breadbaker"];
var firstRegimentKnights = [ *...tons of Knight objects...* ];
var listsForTests = [ firstRegimentNewbs, firstRegimentKnights ];

var BP = function ( listOfParams ){
  for(var i = 0, x = listOfParams[0].length; i < x; i++) {
    listOfParams[1].push(new Knight(listOfParams[0][i], 1));
  }
};

var BPtest = new SpeedTest(noBP, listsForTests, 100000); 
BPtest.startTest();

JavaScript Best Practicies : Performance

Loop Optimization

console.log("You've found the following necklaces:");
for(var i = 0; i < treasureChest.necklaces.length; i++) {
  console.log(treasureChest.necklaces[i]);
}

For each potential loop cycle, the program will need to find and retrieve:
1. the value of i
2. the treasureChest object
3. the necklaces property
4. the array pointed to by the property
5. the length property of the array

1st optimization: Use "cached values" to curtail lengthy, repetitive access to the same data.

console.log("You've found the following necklaces:");
var x = treasureChest.necklaces.length;
for(var i = 0; i < x; i++) {
  console.log(treasureChest.necklaces[i]);
}

Memory access during loop control now only needs to:
1. retrieve the value of i
2. retrieve the value of x
Then, add in our one-time cost in creating x:
1. creating the variable x in memory.
2-5. the 4 steps finding the value of length

Some arithmetic calculations:
5 steps x (10,000 executed loops + 1 check to stop) = 50,005 memory access steps

2 steps x (10,000 executed loops + 1 check to stop) = 20,002 memory access steps
+ 5 extra steps for x = 20,007 memory access steps

~30,000 steps of savings in result!

2nd optimization: place new control variable inside the first loop parameter.

console.log("You've found the following necklaces:");
for(var i = 0, x = treasureChest.necklaces.length; i < x; i++) {
  console.log(treasureChest.necklaces[i]);
}

3rd optimization: avoid repetitive access of a property within an object

console.log("You've found the following necklaces:");
var list = treasureChest.necklaces;
for(var i = 0, x = treasureChest.necklaces.length; i < x; i++) {
  console.log(list[i]);
}

Note: Now we've avoided the extra step of accessing the treasureChest in each cycle.

Script Execution

Problem: Scripts encountered high in the or tags of an HTML page can have adverse effects.
Solution One: Scripts that are not essential to immediate loading of the page should be moved as low as possible.
Solution Two: With external files, the HTML5 async attribute will allow the rest of the page to load before the script runs.

Short Performance Tips

USE A PROTOTYPE FOR SHARED STUFF:
Beware of loading up individual objects with code that easily could be held and sourced elsewhere.
Candidate for optimization:

function SignalFire( ID, startingLogs ){ 
  this.fireID = ID;

  this.logsLeft = startingLogs;
  this.addLogs = function ( numLogs ){
 
    this.logsLeft += numLogs;

  }
  this.lightFire = function () {
 
    alert("Whoooosh!");

  }
  this.smokeSignal = function () {
    if (this.logStatus < this.message.length / 10){
      alert("Not enough fuel to send " +
             "the current message!);
    } else {
      this.lightFire();
      var x = this.message.length;
      for(var i = 0; i

Give all common methods that a "class" of objects will use to the constructor’s prototype.

function SignalFire( ID, startingLogs ){ 
  this.fireID = ID;

  this.logsLeft = startingLogs;
}

SignalFire.prototype = {
  this.addLogs = function ( numLogs ){
 
    this.logsLeft += numLogs;

  }
  this.lightFire = function () {
 
    alert("Whoooosh!");

  }
  this.smokeSignal = function () {
    if (this.logStatus < this.message.length / 10){
      alert("Not enough fuel to send " +
             "the current message!);
    } else {
      this.lightFire();
      var x = this.message.length;
      for(var i = 0; i

USE A DOCUMENT FRAGMENT TO INSERT ADDITIONS ALL AT ONCE:
Adding DOM elements individually ain't always speedy, since each new addition to the DOM causes document "reflow", which can really hinder user experience.

var list = document.getElementById("kotwList");
 
var kotw = ["Jenna Rangespike", 

            "Neric Farthing",
            "Darkin Stonefield"];
for (var i = 0, x = kotw.length; i

Note: Each time the list is appended, we access the DOM and cause an entire document reflow. Not as speedy as we’d like, especially if our list was huge...

Solution:
Use a document fragment to insert additions all at once. Fragments are invisible containers that hold multiple DOM elements without being a node itself.

var list = document.getElementById("kotwList");
 
var kotw = ["Jenna Rangespike", 

            "Neric Farthing",
            "Darkin Stonefield"];
var fragment = document.createDocumentFragment();
for (var i = 0, x = kotw.length; i

Note: Now we add each new li element to the staging fragment, instead of to the document itself. And append all of our new text nodes in one fell swoop, using only one DOM touch!

DECLARE VARIABLES AS FEW TIMES AS POSSIBLE
Every var keyword adds a look-up for the JavaScript parser that can be avoided with comma extensions. Commas used after an initial declaration can signal that you'll declare further variables.

Declaring in loops should thus be used with caution. Anticipate variable needs to avoid the processor burden of creating a new var over and over.

According to all these statements code transforms to:

var list = document.getElementById("kotwList"),
    kotw = ["Jenna Rangespike", 

            "Neric Farthing",
            "Darkin Stonefield"],
    fragment = document.createDocumentFragment(),
    element;
for (var i = 0, x = kotw.length; i

EFFICIENT CHOICES FOR STRING CONCATENATION
Different string building methods will yield different results in terms of execution speed.

The standard concatenation operator has been optimized in most modern browser versions, and is an ideal choice for a small number of string concatenations.

var turn = "";
turn += knight;
turn += action;
turn += weapon;

For concatenations over an array's contents, use the join() method inherited from the Array prototype.

var newPageBuild = [ "", "", "", "

",! ***a hundred or more other html elements***,! "", “", "" ]; page = newPageBuild.join("\n");

Note: The join method concatenates each index of the array, "joined" by any string parameter you pass in. In addition to being faster in tests than many String methods, it is also easier to read.

JavaScript Best Practicies : Syntax

Ternary Conditionals

isArthur && isKing ? 
    (weapon = "Excalibur", helmet = "Goosewhite") : 
        isArcher ? (weapon = "Longbow", helmet = "Mail Helm") : 
        (weapon = "Longsword", helmet = "Iron Helm");;

The “OR” Operator

  • When used in assignment, the OR operator will try to select the first value it encounters that is not “false”.
  • The OR operator takes the leftmost “truth” value, and if none exists, the last “false” value.
var result1 = undefined || 42;
console.log(result1); // 42

var result2 = 0 || ["Item1", "Item2"];
console.log(result2); // ["Item1", "Item2"]

var result3 = "" || {type: "Type", item: "Item"};
console.log(result3); // {type: "Type", item: "Item"}

When all elements are “true”, the FIRST “true” value assigned.

var result1 = "Value" || "Item";
console.log(result1); // "Value"

When all elements are “false”, the LAST “false” value assigned.

var result1 = undefined || "";
console.log(result1); // ""

The “AND” Operator

  • The && operator takes the rightmost “truth” value or the first “false” value.
var result1 = undefined && 42;
console.log(result1); // undefined

var result2 = 0 && ["Item1", "Item2"];
console.log(result2); // 0

var result3 = "" && {type: "Type", item: "Item"};
console.log(result3); // ""

When all elements are “true”, && will return the LAST “true” value found.

var result1 = "Value" && "Item";
console.log(result1); // "Item" 

The Switch Block

function Knight (name, regiment){
 
this.name = name;
 
this.regiment = regiment;
switch (regiment) {

  case 1:

    this.weapon = "Broadsword";
    break;

  case 2:

    this.weapon = "Claymore";
    break;

  case 3:

    this.weapon = "Longsword";
    break;
  case 5:

    this.weapon = "War Hammer";
    break;
  case 6:

    this.weapon = "Battle Axe";
    break;
  case 4:
 
  case 7:
 
  case 8:
    this.weapon = "Morning Star";
    break;
  case "King":"
    this.weapon = "Excalibur";
    break;
  default:
    alert(name + " has an incorrect " + "
        "regiment, Master Armourer!" + "
        "\n\nNo weapon assigned!");

A carefully organized switch block can add LEAST common properties first and MOST common, last.

function ceremonialDagger(knight, rank){
	this.length = 8;
	this.owner = knight;
	switch(rank){
	  case "King": this.diamonds = 1;
	  case "High Constable": this.amethyst = 2;
	  case "Field Marshal": this.sapphires = 4;
	  case "Captain": this.emeralds = 1;
	  case "Knight": this.rubies = 6;
  	}
}

var marshalsDagger = new ceremonialDagger("Timothy", "Field Marshal");
console.log(marshalsDagger); // ceremonialDagger {length: 8, owner: "Timothy", sapphires: 4, emeralds: 1, rubies: 6}