JavaScript

Overview

Data Types

Primitive Types

All primitive types have literal representations of their values.

// use typeof operator to identify primitive types

console.log(typeof "Karlo"); // "string"
console.log(typeof 10); // "number"
console.log(typeof 5.1); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"

Primitive Methods

var name = "Karlo";
var lowercaseName = name.toLowerCase();  // convert to lowercase
var firstLetter = name.charAt(0);  // get first character
var middleOfName = name.substring(2, 5);  // get characters 2-4
var count = 10;
var fixedCount = count.toFixed(2);  // convert to "10.00"
var hexCount = count.toString(16); // convert to "a"
var flag = true;
var stringFlag = flag.toString(); // convert to "true"

Reference Types

Reference values are instances of reference types and are synonymous with objects. Reference types do not store the object directly into the variable to which it is assigned, so the object variable doesn’t actually contain the object instance. Instead, it holds a pointer (or reference) to the location in memory where the object exists. This is the primary difference between objects and primitive values, as the primitive is stored directly in the variable.

Numbers

Math.sin(3.5);
var circumference = Math.PI;

Use parseint() to convert string to an integer:

parseInt("123", 10); // 123
parseInt("010", 10); // 10

//If you don't provide the base, you can get surprising results in older browsers (pre-2013

parseInt("010");  //  8
parseInt("0x10"); // 16

// To convert a binary number to an integer, just change the base

parseInt("11", 2); // 3

// You can also use the unary `+` operator to convert values to numbers:
+ "42";   // 42
+ "010";  // 10
+ "0x10"; // 16

A special value called NaN (short for “Not a Number”) is returned if the string is non-numeric

parseInt("hello", 10); // NaN

Strings

"hello".charAt(0); // "h"
"hello, world".replace("hello", "goodbye"); // "goodbye, world"
"hello".toUpperCase(); // "HELLO"

Other Types

Boolean

Variables

var a;
var name = "Lemmy";

Operators

123 == "123"; // true
1 == true; // true
123 === "123"; // false
1 === true;    // false

Control Structures

var name = "kittens";
if (name == "puppies") {
  name += "!";
} else if (name == "kittens") {
  name += "!!";
} else {
  name = "!" + name;
}
name == "kittens!!"
while (true) {
  // an infinite loop!
}

var input;
do {
  input = get_input();
} while (inputIsNotValid(input))
for (var i = 0; i < 5; i++) {
  // Will execute 5 times
}
var name = o && o.getName();

// Or for setting default values:
var name = otherName || "default";

// JavaScript has a ternary operator for conditional expressions:
var allowed = (age > 18) ? "yes" : "no";
switch(action) {
  case 'draw':
    drawIt();
    break;
  case 'eat':
    eatIt();
    break;
  default:
    doNothing();
}

Objects

var obj = {
  name: "Carrot",
  "for": "Max",
  details: {
    color: "orange",
    size: 12
  }
}
obj.details.color;      // orange
obj["details"]["size"]; // 12
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Define an object
var You = new Person("You", 24);
// We are creating a new person named "You"
// (that was the first parameter, and the age..)

Arrays

var myArray = ["Lucy", 666, false]
myArray.length; // 3
var a = ["dog", "cat", "hen"];
a[100] = "fox";
a.length; // 101

typeof a[90]; // undefined
//  slightly inefficient as you are looking up the length property once every loop.
for (var i = 0; i < a.length; i++) {
  // Do something with a[i]
}

// An improvement is this:
for (var i = 0, item; item = a[i++];) {
  // Do something with item
}

// Here we are setting up two variables. The assignment in the middle part of the for loop is also tested for truthfulness — if it succeeds, the loop continues. Since i is incremented each time, items from the array will be assigned to item in sequential order. The loop stops when a "falsy" item is found (such as undefined).
["dog", "cat", "hen"].forEach(function(currentValue, index, array) {
  // Do something with currentValue or array[index]
});

Array Methods

Functions

function add(x, y) {
  var total = x + y;
  return total;
}
add(); // NaN
// You can't perform addition on undefined
add(2, 3, 4); // 5
// added the first two; 4 was ignored
function add() {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum;
}

add(2, 3, 4, 5); // 14
avg.apply(null, [2, 3, 4, 5]); // 3.5
// This is semantically equivalent to the function avg() form

var avg = function() {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum / arguments.length;
};
var a = 1;
var b = 2;

(function() {
  var b = 3;
  a += b;
})();

a; // 4
b; // 2
function countChars(elm) {
  if (elm.nodeType == 3) { // TEXT_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++) {
    count += countChars(child);
  }
  return count;
}
var charsInBody = (function counter(elm) {
  if (elm.nodeType == 3) { // TEXT_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++) {
    count += counter(child);
  }
  return count;
})(document.body);

Custom Objects

function makePerson(first, last) {
  return {
    first: first,
    last: last
  };
}
function personFullName(person) {
  return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
  return person.last + ', ' + person.first;
}

s = makePerson("Lemmy", "Kilmister");
personFullName(s); // "Lemmy Kilmister"
personFullNameReversed(s); // "Kilmister, Lemmy"
function makePerson(first, last) {
  return {
    first: first,
    last: last,
    fullName: function() {
      return this.first + ' ' + this.last;
    },
    fullNameReversed: function() {
      return this.last + ', ' + this.first;
    }
  };
}

s = makePerson("Lemmy", "Kilmister")
s.fullName(); // "Lemmy Kilmister"
s.fullNameReversed(); // "Kilmister, Lemmy"

Object.prototype methods

this keyword

s = makePerson("Lemmy", "Kilmister");
var fullName = s.fullName;
fullName(); // undefined undefined
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = function() {
    return this.first + ' ' + this.last;
  };
  this.fullNameReversed = function() {
    return this.last + ', ' + this.first;
  };
}
var s = new Person("Lemmy", "Kilmister");
function personFullName() {
  return this.first + ' ' + this.last;
}
function personFullNameReversed() {
  return this.last + ', ' + this.first;
}
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = personFullName;
  this.fullNameReversed = personFullNameReversed;
}
function Person(first, last) {
  this.first = first;
  this.last = last;
}
Person.prototype.fullName = function fullName() {
  return this.first + ' ' + this.last;
};
Person.prototype.fullNameReversed = function fullNameReversed() {
  return this.last + ', ' + this.first;
};
s = new Person("Lemmy", "Kilmister");
s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function

Person.prototype.firstNameCaps = function firstNameCaps() {
  return this.first.toUpperCase()
};
s.firstNameCaps(); // "Lemmy"
var s = "Lemmy";
s.reversed(); // TypeError on line 1: s.reversed is not a function

String.prototype.reversed = function reversed() {
  var r = "";
  for (var i = this.length - 1; i >= 0; i--) {
    r += this[i];
  }
  return r;
};

s.reversed(); // ymmeL
"This can now be reversed".reversed(); // desrever eb won nac sihT
var s = new Person("Lemmy", "Kilmister");
s; // [object Object]

Person.prototype.toString = function() {
  return '<Person: ' + this.fullName() + '>';
}

s.toString(); // "<Person: Lemmy Kilmister>"
function trivialNew(constructor, ...args) {
  var o = {}; // Create an object
  constructor.apply(o, args);
  return o;
}

Calling

var bill = trivialNew(Person, "William", "Orange");

is therefore almost equivalent to

var bill = new Person("William", "Orange");
function lastNameCaps() {
  return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Is the same as:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps();

call vs apply vs bind

Use .bind() when you want a function to later be called with a certain context, useful in events. Use .call() or .apply() when you want to invoke the function immediately, and modify the context.

Call/apply call the function immediately, whereas bind returns a function that when later executed will have the correct context set for calling the original function. This way you can maintain context in async callbacks, and events.

A simple implementation of bind would be:

Function.prototype.bind = function (scope) {
    var fn = this;
    return function () {
        return fn.apply(scope);
    };
}

Inner functions

function betterExampleNeeded() {
  var a = 1;
  function oneMoreThanA() {
    return a + 1;
  }
  return oneMoreThanA();
}

Closures

function makeAdder(a) {
  return function(b) {
    return a + b;
  };
}
var x = makeAdder(5);
var y = makeAdder(20);
x(6); // 11
y(7); // 27

Reference

A re-introduction to JavaScript