Syntax

Ternary Conditionals

var conditional = someCondition ? pickIfTrue : pickIfFalse;

Can be used in string concatenation. However, need to place in ( ):
console.log("Some stuff" \+ ( someCondition ? pickIfTrue : pickIfFalse) );

'?' has lower precedence than '+' concatenation operator.  If ? follows a +, the ? looks for a boolean but would find a string.  In JS, any value that is not false, 0, undefined, NaN, "", or null evaluates to truthy.  So, otherwise would evaluate to truthy.

Compound ternary conditional work:
var conditional = isTrue && isFalse ? pickIfTrue : pickIfFalse;

Ternaries can take action in the results.  Any executable can work in the left or right, including immediately invoked functions.

Multiple actions may be taken in each result of a ternary conditional.  Multiple statements are grouped in commas, and wrapped in parentheses.

Ternaries may also be nested. A ternary can hold other ternaries within each of the possible responses.

Logical Assignment

OR ||

Create a new empty array if no swords property, otherwise return existing swords property:

var armoury = { addSword: function (sword) {
  this.swords = this.swords ? this.swords : [];
  this.swords.push(sword);
  }
};

Can be re-written with logical assignment. When || used in an assignment, will return first truthy value.  If first value truthy, second value won't be looked at (known as short-circuiting):

var armoury = { addSword: function (sword) {
  this.swords = this.swords || [];
  this.swords.push(sword);
  }
};

If neither OR is truthy, logical assignment will take last value even if falsy:
var result = undefined || ""; // result returns ""

AND &&

The && operator takes the rightmost truthy value or the first falsy value.

When all elements are truthy, && will return the last truthy value. When all elements are falsy, && will return the first falsy value found.

&& operator is useful in contingent assignments.  Something must be true for subsequent assignment to occur.

The && operator lets us check multiple conditions before allowing assignment.

Array.prototype.indexOf(item); returns the index of the first found instance in the array, or -1.  Therefore can check if in an array using <= 0.
Array.prototype.splice( 1, 2 ) splices at index number, for number of items.  Returns the spliced items in new array.

armoury.retrieveSword = function (request) {
  return (this.swords.indexOf(request) >= 0) ?
    this.swords.splice(this.swords.indexOf(request), 1)[0] :
    alert("No " \+ request);
}
var isKnight = true;
var weapon = isKnight && armoury.retrieveSword("Sword");

isKnight is true, so conditional will keep checking and take second value.

Switch Block

Without break;, switch will fall-through all the way to final case (including a default case). Fall-through can be used to stack cases on top of each of if require multiple cases to result in same action.

Here, both 2 and 3 will result in var thing = 'that':

var thing;
switch (expression) {
  case 1:
    thing = "that";
    break; 
  case 2:
  case 3:
    thing = "that";
    break;
  default:
    thing = "default";
}

Without default case, no action will be taken if there's is no match.

Fall-through allows hierarchical code execution.  Add least common properties first and most common properties last.  Using fall-through, additional properties can be added to later, more common properties.

switch (rank) {
  case "King": this.diamonds;
  case "Knight": this.gold;
  case "Soldier": this.silver;
}

Here, King would fall-through and receive diamonds, gold and silver.  Whereas Soldier would just receive silver.

Performance

Loop Optimisation

Standard loop implementation:

for (var i = 0; i < object.propertyArray.length; i++) {
  console.log(object.propertyArray[i]);
}

At the start of each potential loop cycle, program will need to find and retrieve:
- value of i
- the object
- the propertyArray property
- the array pointed to by the property
- the length property of the array.

Alternatively, can use cached values to curtail lengthy, repetitive access to the same data.  Key is to avoid repetitive access at depth. Processor savings mean more speed.  If looping large amounts of data, change will have significant impact.

This will use all steps to look up object.propertyArray.length, but only going to happen once rather than every loop cycle:

var list = object.propertyArray; // won't need to look up this each log
for (var i = 0, x = list.length; i < x; i++) {
  console.log(list[i]);
}

Use for loops rather than for-in loops when goal is only to reach every index.  This is because a for-in loop will add all the methods that have been added to the Array prototype.  This is because methods added to prototype are enumerable, just like indices of the array.

Script Performance

After retrieving webpage, browser will parse document.  Parsing has large impact on performance.

A browser can download 6 assets in parallel (style sheets, images etc).  However, upon finding a script,  browser will stop 6 parallel downloads until script has finished loading.

Scripts high in the HTML in or can have adverse impact on performance.  Will run all processes on script first, then load rest of page.

Solutions:
1. Reload work intensive scripts low in document parsing.  Anything not related to immediate presentation like css.  A good place is immediately before the end of closing body tag.
2. Use HTML5 async attribute inside script tag, which allows rest of page to load before script runs (using async tag can place script anywhere in document)

<script src="..." async></script>

Performance Hints

Inheritance

Inheritance can improve memory efficiency.  Beware of loading up individual objects with code that could be held and sourced elsewhere in a prototype.  I.e. use a prototype for all shared methods.

DOM Performance

Adding individual DOM elements is not performant.  Each new additional to the DOM causes document 'reflow', which is slow and hinders user experience.

For example, element.appendChild("li") in a for loop, will cause a document reflow every loop because each time an element is appended the DOM is accessed.  Particularly problematic if the list is long.

Instead, 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 fragment = document.createDocumentFragment();
fragment.appendChild(li); // add each li to fragment as staging area rather than DOM itself
element.appendChild(fragment); // then append the fragment in one process.

Declare Variables as seldom as possible

Every var requires a lookup for parser which is costly:
- Include multiple variables with commas in one statement.
- Avoid declaring variables in loops.  Var will get looked up every loop.  Declare outside loop, then reference within.

Use efficient choices when deciding how to concatenate strings

The standard concatenation operator (+=) has been optimised in modern browsers, and is ideal choice for small number of string concatenation.

If strings inside an array (such as when creating HTML elements from array), use Array.prototype.join() method. join is faster for arrays than standard string operator.

Measuring Performance

console.time console.timeEnd

console.time method that will assess the time code will take to run.

console.time("arg") to start timer.  console.timeEnd("arg") to end.  To unite timer, parameter string must match.

Time will help determine which code produces best experience for users.

Can use as many as like at once, matching up with string parameters.

console.time will produce different results each time, so for overall indication of performance average out results. Also console.time usage itself impacts performance.  Otherwise its good for showing guide of performance.

Creating Speed Class which will average length of time code takes to run

Must retrieve and use actual numerical time data using Date() object.

Unary operator with Date object gives time in milliseconds. Same as declaring new Number object on rightNow - new Number (rightNow):
var rightNow = +new Date();

Issues

Comparisons

Strict equality

=== compares type and content (strict equality). == converts type.

instanceof

Verifying an Object's class: is an object built by a Constructor or has a specific Prototype?

if (object instanceof constructor) {
... do something ...
}

Can use instanceof to check entire inheritance chain.  An object is an instance of all prototypes from which it inherits properties.

Exceptions

An exception is a run-time error, whereas a syntax error won't compile.

Thinking about exceptions (run-time errors), need to control program flow after exceptions.  JavaScript offers a method for identifying and recovering from exceptions.

Error types allow for specific types of action.
- ReferenceError: something hasn't been defined
- TypeError: something is the wrong type for the method getting called

Use conditionals and the throw keyword to craft exception scenario based on expectations.  Throw keyword if reached, will throw execution to the catch block to handle.

The Finally block follows try/catch, and holds code that should run whether errors present or not.

try {
  var newHallFame  = ['man', 'woman'];  
  if (list === undefined){
    throw new ReferenceError(); 
  }     
  if ((list instance Array) === false){
    throw new TypeError(); 
  }
  list.concat(newHallFame);  
} catch (error) {
  if (error instanceof ReferenceError) {
    alert("ReferenceError: " \+ error);
  } 
  if (error instanceof TypeError) {
    alert("TypeError: " \+ error);
  }
} finally {
  console.log(list);
}

Can nest try blocks within catch blocks to organise sequence.

Do Nots

with

Don't use with keyword.  Anything created within a with block, will create on global scope, not within the scope of the with.  Instead, variable cache to avoid typing out long-winded objects.

eval

Eval affects legibility, debugging and performance. Eval method takes a string as a parameter, starts the compiler and treats string as if a line of code to execute.

Better to use an array. Shouldn't use eval to parse JSON, use JSON.parse().

Don't leave off curly {}

Just because can leave curly {} off single-statement lines of code, doesn't mean should.

Numbers

JavaScript uses binary floating point values to handle all of its decimal operations.
console.log(0.1 + 0.2); // returns 0.30000000000000004

toFixed() allows to set the number of decimal points to display. However will return a string, not a number.
parseFloat() allows to use values of exact length in other maths operations.

function tax (price, percent) {
  // will return 2 decimal point number
  return parseFloat((price*percent/100)).toFixed(2));
}

parseInt() will convert numerical strings to integers. Won't round up.  Will accept octal, hexadecimal and decimal values.
parseFloat() will convert numerical string to floating point number.

If pass a string that not starting with numbers, parseInt() and parseFloat() will return NaN.

Use a radix value to tell parseInt which base system to use:
parseInt("021", 10); // 10 here refers to decimal system.

NaN

Don't use NaN alone to check type:
typeof NaN; // returns "number"
console.log(NaN === NaN); // returns false
isNaN("42"); // returns false: this method is strictly looking for value NaN

Use typeof && NaN:
typeof data === "number" && !isNaN(data) // check if type number and is not value NaN.

Modularity

Namespacing

Conflicts global elements between JS files can cause overwrites. Variables of the same name may be overwritten because of hoisting.

Namespacing, although not native to JS, can help limit global impact and provide data protection.

Key to creating a namespace is a single Global object, commonly called the 'wrapper'.  By convention, this can be written in CAPS.  This becomes a global container for data and functionality.  Nested namespacing is frequently used in module pattern.

Public / private modules

Any code that knows the namespaces, can access any object or method.  May want privacy to data inside namespace:
1. Decide which data should be public and which should be private. Public methods and values often trigger private methods and values.
2. Closures allow us to "privatise" properties.
- Wrap the entire set of properties in an anonymous immediately invoked function expression.
- make desired private properties into local executable code by creating as variables. These local values and methods will be closed into the namespace.  They're then local variables to the namespace, rather than public.
- return an object, which will add the public properties to their own object, which will become the PUBLIC namespace.  Because the function expression is immediately called, the returned object will be handed immediately to the PUBLIC var and become the namespace.

var PUBLIC = (function() {
  var private = [];
  var private2 = function () {};

  return {
    publicMethod: function() {}
  };
})();

Closure produces desired private methods and values.  All the local variables are bound down within the scope of the returned namespace object.  However never actual properties within the returned namespace object.  But they are there in the closure, visible to and able to be referenced by the members of the local namespace scope.

Global variables

Standard global use in a module pattern causes two problems:
1. When non-local variables are referenced in a module, the entire length of the scope chain is checked.
2. Lengthy namespaces mean that global variables have unclear scope, leading to tough to manage code.

Instead, use global imports: faster local imports and clearer code. Pass globals into the IIFE using the calling parentheses. Then include in the parameter to the IIFE.

var globalVariable = "something";
var PUBLIC = (function( global ) {
  var private = [];
  var private2 = function () {};

    return {
      publicMethod: function() {
        // use the global 
      }
    };
})(globalVariable);

This imported variable is closed up as a local variable, just like any another local variable.

Augmentation

Modules often need to have additions to their existing properties.  There is value in splitting functionality between files.

Augmentation provides extra properties for existing modules.  Essentially works by updating existing global namespace variable.

Results in an object that gets returned and stored back in the old public namespace.

NOTE: Previous private data will not be accessible to the new properties.  Therefore, best practice is to group file contents around needed data.

Note no var on PUBLIC:

PUBLIC = (function( oldNamespace ) {
  var privateVar = [];
  oldNamespace.newMethod = function() {};

  return oldNamespace;

})( PUBLIC );