Important JavaScript Notes

Variables

Variables declaration are hoisted.

Everything acts like an object except for the two non-values.

Primitive Values

Casting

+x // cast to Number
'' + x // cast to String
!!x // cast to Boolean (useful for strict compare NaN)

Characteristic of Primitive Values

Booleans

true/false

Always false:

So use:

if (!x) {}

Binary Logical Operator:

Numbers

'NaN' and 'infinity' are only for integer type

Strings

'abcde'.length // 5
'abcde'[3] // d

Non-values (undefined, null)

'undefined' means 'var' is declared but not initialized. However, note that 'typeof' a variable that has yet been declared ('var') is 'undefined'.

'null' means the variable is declared and being initialized to no value.

Objects (Non-primitive Values)

Characteristic of Objects

Plain Objects

var obj = {};

Arrays

Array is a subset of object.

var arr = [];

'for..in' is slow, use traditional 'for' instead.

arr.length can be used to truncate array.

Regular Expressions

Regex literals: /regex/

typeof and instanceof

typeof 'test' // string

typeof foo !== 'undefined'

typeof is inconsistent. The only useful case of typeof is to check whether a variable is defined.

test instanceof Class

instanceof is only useful for comparing custom-made objects. Useless when used on built-in types.

Function Declaration vs Function Expression.

Cleanup:

obj.property = null;

better than

delete obj.property

Closures

Closure, close free variables of parent scope. Garbage collection is deferred because of closure.

A simple explanation for closures:

  1. Take a function. Let's call it F.

  2. List all the variables of F.

  3. The variables may be of two types:

    1. Local variables (bound variables)
    2. Non-local variables (free variables)
  4. If F has no free variables then it cannot be a closure.

  5. If F has any free variables (which are defined in a parent scope of F) then:

    1. There must be only one parent scope of F to which a free variable is bound.
    2. If F is referenced from outside that parent scope, then it becomes a closure for that free variable.
    3. That free variable is called an upvalue of the closure F.

Example:

function countSeconds(howMany) {
  for (var i = 1; i <= howMany; i++) {
    // Here, the outer and the inner variable have the same name.
    // The outer one won't be accessible any more, but that's okay.
    (function (i) {
      setTimeout(function () {
        console.log(i);
      }, i * 1000);
    }(i));
  }
};
countSeconds(3);

The above is the same as the one below. Both are closure. The last one uses bind().

function countSeconds(howMany) {
  for (var i = 1; i <= howMany; i++) {
    // The IEF is put right into the setTimeout
    // and returns the actual timeout handler
    setTimeout((function (i) {
      return function () {
        console.log(i);
      };
    }(i)), i * 1000);
  }
};
countSeconds(3);

function countSeconds(howMany) {
    for (var i = 1; i <= howMany; i++) {
    setTimeout(console.log.bind(console, i), 1000);
    }
}
countSeconds(3);

'for' loop does not create closure:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + list[i];
        result.push(function() {console.log(item + ' ' + list[i])});
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

OOP

Public and private members, see: http://www.crockford.com/javascript/private.html

Class

function People(first, last) {
    this.first = first;
    this.last = last;
}

People.prototype.hello = function() {
    return 'Hello, ' + this.first + ' ' + this.last;
}

var JohnSmith = new People('John', 'Smith');
JohnSmith.first;
JohnSmith.hello();

Inheritence (Prototype Chain)

// Jobs inherits from People
function Jobs() {}
Jobs.prototype = new People();
Jobs.prototype.constructor = Jobs;
var doctor = new Jobs()

Module

// Everything concerning the main navigation in here:
var mainNavigation = (function () {
  // private property
  var currentIndex = 0;
  var maxIndex = 2;

  // private method
  var moveTo = function (index) {
    if (index < 0 || index > maxIndex) return;
    currentIndex = index;
    // Animate to page number [currentIndex] here
  }

  // public method
  return {
    next: function () {
      moveTo(currentIndex + 1);
    },
    previous: function () {
      moveTo(currentIndex - 1);
    },
    moveTo: moveTo
  };
}());

this

'this' can be bound to:

Recommended to only use this for method and constructor.

Scope

Javascript only has function scope. Not to be mistaken for {} block scope.

Name resolution order:

  1. In case there is a var foo statement in the current scope, use that.
  2. If one of the function parameters is named foo, use that.
  3. If the function itself is called foo, use that.
  4. Go to the next outer scope, and start with #1 again.

Namespaces

(function() {})();

setTimeout vs setInterval

setInterval is non-blocking and inaccurate. Avoid.

Use setTimeout recursion:

function foo() {
    var id = setTimeout(foo, 1000);
}
foo();
clearTimeout(id);