Intro to JavaScript from Java

This introduction attempts to explain JavaScript to those with prior programming experience in Java, and will point out some common mistakes along the way. It is just for the JavaScript language and doesn't contain anything browser-related. The code examples use an arrow to indicate code that is typed in, which will be immediately followed by the result if there is one:

→ 2 + 2;
  4

Even if you have already used JavaScript, there will hopefully still be some useful information (especially towards the end). It is meant to be read from top to bottom but can also be used as a reference.

Table of contents

Booleans
Strings
Numbers
Regular Expressions
Variables
Objects
Arrays
Functions
Classes/Prototypes
Subclasses
Tricks

Booleans

Comparisons evaluate to true and false, just like in Java:

→ 1 != 2;
  true
→ 1 == 2;
  false

JavaScript also has the strict equality operators === and !==, which don't attempt to convert types before comparison. These should be preferred over == and != because they have a lower potential for bugs:

→ 0 == "";
  true
→ 0 === "";
  false
!

JavaScript has some very weird comparisons. For example, null >= 0 and null <= 0 but null != 0. Be careful of these quirks, especially when values may be null.

Strings

A String object is like a String in Java (they both use 16 bits per character). Strings can be declared using either single or double quotes. There is no separate character type in JavaScript, so instead characters are just strings of length 1. Strings in JavaScript and Java share many similar methods:

→ "hello".length;
  5
→ "hello".charAt(0);
  "h"
→ "hello, world".toUpperCase();
  "HELLO, WORLD"
→ "a" + "b" + 2;
  "ab2"

JavaScript strings are immutable (they can't be modified). A mutable operation such as changing a character is performed by constructing a new string with that character changed.

!

You can also index into a string using str[0] instead of str.charAt(0), but the bracket syntax doesn't work in versions of IE before IE 8.

Documentation for String

String

instance.length
Returns the number of characters in the string.
instance.charAt(index)
Returns the character at index, where 0 <= index < instance.length.
instance.charCodeAt(index)
Returns a number indicating the Unicode value of the character at index, where 0 <= index < instance.length.
instance.indexOf(str)
Returns the first index of the string str in instance, or -1 if str could not be found.
instance.lastIndexOf(str)
Returns the last index of the string str in instance, or -1 if str could not be found.
instance.match(regexp)
Matches the regular expression regexp inside instance. If regexp includes the g (global) flag, this returns an array of all matches, otherwise this is the same as regexp.exec(instance).
instance.replace(regexp or substr, replacement or replacementFunction)
Replaces the first match inside instance of the regular expression regexp or the string substr with the string replacement or the result of calling the function replacementFunction. This replaces all matches if regexp includes the g (global) flag.

If replacement is specified, it may contain one of these special values:

  • $$: inserts a "$"
  • $&: inserts the matched substring
  • $`: inserts the substring of instance before the match
  • $': inserts the substring of instance after the match
  • $n: inserts the nth parenthesized submatch starting at 1, provided regexp was specified

If replacementFunction is specified, it will be called once per match to determine the replacement string with the arguments (match, p1, p2, p3, ..., index, string) where:

  • match: the substring for the current match
  • p1, p2, p3, ...: the parenthesized submatches, provided regexp was specified
  • index: the index of the match inside instance
  • string: the entire string (instance)
instance.search(regexp)
Returns the index of the first match of the regular expression regexp in instance, or -1 if there were no matches.
instance.slice(start[, end])
Returns the substring starting at the index start up to but not including the index end. If end is omitted it defaults to the length of instance. Negative indices indicate an offset from the end of the string, so "'xyz'".slice(1, -1) == "xyz".
instance.split(separator[, limit])
Returns an array of substrings from splitting instance by the string separator. If limit is specified, a maximum of that number of results are returned. The separator can also be a regular expression.
→ "abc".split("")
  ["a", "b", "c"]
→ "game of life".split(" ", 2)
  ["game", "of"]
instance.substr(index[, length])
Returns the substring starting at index and containing length characters. If length is omitted it defaults to instance.length - index.
instance.toLowerCase()
Returns the lowercase version of instance. Some locales may need the toLocaleLowerCase() method instead.
instance.toUpperCase()
Returns the uppercase version of instance. Some locales may need the toLocaleUpperCase() method instead.
String.fromCharCode(code1[, code2, code3, ...])
Returns the string where the nth character has the Unicode value coden.

Numbers

A Number object is like a double in Java, which is a 64-bit floating-point value. Strings can be converted to numbers using the parseFloat function. JavaScript has no int type, although you can get rid of the fractional part of a number with the parseInt function (which works on both numbers and strings).

!

Numbers that start with a zero are in octal. The parseInt function takes a second argument that is the radix to use, so always pass in 10 as the second argument or your zip codes will end up in octal:

→ parseInt("04105");
  2117
→ parseInt("04105", 10);
  4105

JavaScript also has NaN (not a number) and Infinity, which are returned by some computations:

→ parseInt("text", 10);
  NaN
→ 1 / 0;
  Infinity
→ -1 / 0;
  -Infinity

Documentation for Number and Math

Number

instance.toExponential([digits])
Returns the string representation of instance in exponential notation with digits number of digits after the decimal point. When omitted, digits defaults to as many digits as necessary.
instance.toFixed([digits])
Returns the string representation of instance with digits number of digits after the decimal point. When omitted, digits defaults to 0.
instance.toString()
Returns the string representation of instance.
Number.MIN_VALUE
5e-324 (the smallest positive number that JavaScript can represent)
Number.MAX_VALUE
1.7976931348623157e+308 (the largest positive number that JavaScript can represent)

Math

Math.E, Math.LN2, Math.LN10, Math.LOG2E, Math.LOG10E, Math.PI, Math.SQRT1_2, Math.SQRT2
Various built-in constants.
Math.abs(n)
Returns the absolute value of n.
Math.sin(n), Math.cos(n), Math.tan(n)
Trigonometric functions that return the sine, cosine, and tangent, respectively. The angle n must be in radians (radians = degrees * Math.PI / 180).
Math.asin(n), Math.acos(n), Math.atan(n)
Trigonometric functions that return the inverse sine, inverse cosine, and inverse tangent, respectively. Returned angles are in radians (degrees = radians * 180 / Math.PI).
Math.atan2(y, x)
Returns the inverse tangent of y / x in radians. This avoids the divide-by-zero errors and quadrant ambiguities present in Math.atan().
Math.round(n)
Returns n rounded to the nearest integer.
Math.floor(n)
Returns the greatest integer less than or equal to n.
Math.ceil(n)
Returns the least integer greater than or equal to n.
Math.pow(base, exponent)
Returns base raised to exponent.
Math.exp(n)
Same as Math.pow(Math.E, n).
Math.log(n)
Returns the natural logarithm of n (inverse of Math.exp).
Math.min(a, b)
Returns the minimum of a and b.
Math.max(a, b)
Returns the maximum of a and b.
Math.random()
Returns a pseudorandom number between 0 inclusive and 1 exclusive. There is no way to set the pseudorandom seed.

Regular Expressions

JavaScript has built-in support for regular expressions. A regular expression can either be created using the literal syntax or using the RegExp constructor:

→ /\d+/.exec("abc");
  null
→ /\d+/.exec("123");
  ["123"]
→ new RegExp("^(.*)(\\d)$").exec("five5");
  ["five5", "five", "5"]

See Mozilla's documentation for more details.

Variables

A variable is declared with the var keyword:

→ var a = null;
→ a;
  null

Uninitialized variables are set to a special value called undefined:

→ var a;
→ a;
  undefined
!

Every JavaScript environment includes an uninitialized variable named undefined by default. Many people check if a variable is initialized by testing for equality with undefined (making sure to use === instead of == because null == undefined):

→ var x;
→ x === undefined;
  true
→ x = 2;
→ x === undefined;
  false

This usually works, except when someone initializes undefined. The correct way to check if a variable is initialized is to use the typeof operator:

→ var x;
→ x === undefined;
  true
→ undefined = 2;
→ x === undefined;
  false
→ typeof x == "undefined";
  true

Objects

An Object is similar to a HashMap<String, Object> in Java. Objects can be easily defined using the object literal syntax, and properties can be accessed using brackets or a dot:

→ var object = { a: 3.14159, b: "text" };
→ object["a"];
  3.14195
→ object.b;
  "text"

Object properties can be added via assignment, removed via delete, and queried via in:

→ var object = {};
→ object.a = 2;
→ "a" in object;
  true
→ delete object.a;
→ "a" in object;
  false

The properties in an object can be iterated through using a for-in loop:

→ var object = { a: 1, b: 2 }, total = 0;
→ for (var propertyName in object) {
    total += object[propertyName];
  }
→ total;
  3
!

Objects are an unordered mapping. Although all major JavaScript engines iterate over object properties in the order they were created, the JavaScript language doesn't specify any iteration order. It has been the case before that some JavaScript engines (for example, early versions of Chrome) iterate in a different order, and it's considered bad practice to rely on this order.

!

Be careful when using objects as a map with arbitrary keys. Some browsers (Firefox and Chrome) have a special property named __proto__ that will cause undesired behavior in this case when overwritten. Understanding why requires knowledge of prototypes which we haven't gotten to yet. One solution is to prefix all keys with a character not present in identifiers such as space before using the key to find a property:

→ var map = {}, key = '__proto__';
→ map[key] = 1;
→ map[key];
  Object.prototype
→ map[' ' + key] = 1;
→ map[' ' + key];
  1

Prototypes may also cause extra properties to show up when using the in operator or for-in loops. To avoid all of these complications you'll want to use something like this, which uses the hasOwnProperty() method to check if a property is really defined on that object:

→ function hasKey(obj, key) {
    return obj.hasOwnProperty(' ' + key);
  }
→ function getKey(obj, key) {
    return hasKey(obj, key) ? obj[' ' + key] : null;
  }
→ function setKey(obj, key, value) {
    obj[' ' + key] = value;
  }
→ var map = {}, key = '__proto__';
→ getKey(map, key);
  null
→ setKey(map, key, 1);
→ getKey(map, key);
  1

Arrays

Arrays can be created using the array literal syntax and indexed into using brackets. Arrays are actually just objects with properties that happen to be numbers and with a special length property:

→ var array = [3.14159, "text"];
→ array[0];
  3.14159
→ array["0"];
  3.14159
→ array.length;
  2

Inserting and removing elements from an array is all done with the splice() method, although there are special-case methods for adding and removing from the beginning and end of an array (push(), pop(), shift(), and unshift()). The filter() and map() methods are also very useful for concise array manipulations.

Documentation for Array

Array

instance.length
Returns the number of elements in the array. Assigning to this value changes the length of the array, filling in newly created elements with undefined.
instance.splice(index, count[, a, b, c, ...])
Removes count elements starting at index and optionally inserts more elements at index if more arguments are provided. Returns an array containing the removed elements.
→ var x = [1, 2, 3, 4, 5];
→ x.splice(2, 1); // remove the element at index 2
  [3]
→ x.splice(1, 0, 1.5); // insert 1.5 at index 1
  []
→ x.splice(3, 1, 4.5); // replace the element at index 3 with 4.5
  [4]
→ x;
  [1, 1.5, 2, 4.5, 5]
instance.push(a[, b, c, ...])
Appends one or more elements to the end of instance. Returns the new length of instance.
instance.pop()
Removes and returns the last element of instance. Returns undefined if instance is empty.
instance.unshift(a[, b, c, ...])
Prepends one or more elements to the beginning of instance. Returns the new length of instance.
instance.shift()
Removes and returns the first element of instance. Returns undefined if instance is empty.
instance.slice(start[, end])
Returns an array containing all elements starting at the index start up to but not including the index end. If end is omitted it defaults to the length of instance. Negative indices indicate an offset from the end of the array, so [1, 2, 3, 4].slice(1, -1) == [2, 3].
instance.concat(a[, b, c, ...])
Returns a new array with all the elements of instance followed by the provided arguments. If an argument is an array then each element of that array is added separately. Note that instance is not modified.
→ [1, 2, 3].concat([4, 5, 6], 7, [8, 9])
  [1, 2, 3, 4, 5, 6, 7, 8, 9]
instance.sort([compareFunction])
Sorts the elements of instance and returns instance. The optional function compareFunction(a, b) should return a negative number, zero, or a positive number if a comes before b, is equal to b, or comes after b, respectively. If compareFunction is not specified, the elements are sorted lexicographically by their string representations (so this will not sort numerically, as "80" comes before "9"). Sorting numerically is as easy as returning a - b from compareFunction. Note that instance is modified.
instance.reverse()
Reverses the elements of instance and returns instance. Note that instance is modified.
instance.join([separator])
Returns a string created by concatenating all elements of instance together, each separated by the string separator. If separator is omitted it defaults to ",".
→ [1, 2, 3].join(" : ");
  "1 : 2 : 3"
instance.indexOf(element[, fromIndex])
Returns the first index of element in instance starting from fromIndex and counting up. If fromIndex is negative it is relative to the end of the array, and if fromIndex is not specified it defaults to 0.
instance.lastIndexOf(element[, fromIndex])
Returns the last index of element in instance starting from fromIndex and counting down. If fromIndex is negative it is relative to the end of the array, and if fromIndex is not specified it defaults to instance.length.
instance.filter(callback[, thisObject])
Calls callback once for each element in instance and returns an array containing elements for which callback returned true. If provided, thisObject specifies the object to use as this inside callback. The callback function is called with three arguments: the current element, the index of the current element, and the entire original array (instance). Note that instance is not modified.
→ [2, -1, 0, 4].filter(function(x) {
    return x > 0;
  });
  [2, 4]
instance.map(callback[, thisObject])
Calls callback once for each element in instance and returns an array of the return values. If provided, thisObject specifies the object to use as this inside callback. The callback function is called with three arguments: the current element, the index of the current element, and the entire original array (instance). Note that instance is not modified.
→ [1, 2, 3].map(function(x) {
    return x.toString();
  });
  ["1", "2", "3"]
instance.forEach(callback[, thisObject])
Calls callback once for each element in instance. If provided, thisObject specifies the object to use as this inside callback. The callback function is called with three arguments: the current element, the index of the current element, and the entire original array (instance).
instance.every(callback[, thisObject])
Returns true only if callback returns true for each element in instance (including when instance is empty). If provided, thisObject specifies the object to use as this inside callback. The callback function is called with three arguments: the current element, the index of the current element, and the entire original array (instance).
instance.some(callback[, thisObject])
Returns true if callback returns true for at least one element in instance. If provided, thisObject specifies the object to use as this inside callback. The callback function is called with three arguments: the current element, the index of the current element, and the entire original array (instance).

Functions

Functions are declared using the function keyword and have no type information for their arguments or return type. A function doesn't have to be named, in which case it's an anonymous function:

→ function f(x) { return x * x; }
→ var g = function(y) { return Math.sqrt(y); };

Variable scope is different from Java. There is only one scope per function and blocks do not introduce a child scope:

→ function func() {
    if (true) {
      var x = 2;
    }
    return x;
  }
→ f();
  2

Variables declared in the global scope are actually stored as properties on the window object:

→ var x = 2;
→ window.x;
  2
!

In a function, assigning to a variable without declaring it using var will create a global variable. This is almost always unintentional and makes finding typos difficult. Even though leaving off the var keyword may work, it is considered bad practice and you should always declare all variables with var before using them. If you do want to create a global variable from within a function, explicitly assign it as a property of the window object instead.

A function is a Function object, meaning it can be treated like any other data type. New functions can be constructed dynamically via the Function constructor:

→ var add = new Function("x", "y", "return x + y;");
→ add(1, 2);
  3

Functions can also be declared inside other functions, in which case they have access to the variable scope of the outer function. JavaScript functions are closures, which means they retain access to all variables they had access to when they were defined:

→ function createAdder(value) {
    return function(x) {
      return x + value;
    };
  }
→ var adder = createAdder(3);
→ adder(2);
  5
!

This fact is the cause of a common error:

→ var funcs = [];
→ for (var i = 0; i < 3; i++) {
    var value = i * 2;
    funcs.push(function() {
      return value;
    });
  }
→ funcs[0]();
  4

One might expect funcs[0]() to be 0 instead of 4. This is a consequence of single scope and closures, since the anonymous function captures the variable value which is set to 4 by the time the loop finishes. The fix is to create a new scope using another anonymous function that is called immediately:

→ var funcs = [];
→ for (var i = 0; i < 3; i++) {
    (function(value) {
      funcs.push(function() {
        return value;
      });
    })(i * 2);
  }
→ funcs[0]();
  0

Functions can have a variable number of arguments, which can be accessed using the magic arguments variable:

→ function sum() {
    var total = 0;
    for (var i = 0; i < arguments.length; i++) {
      total += arguments[i];
    }
    return total;
  }
→ sum(1, 2, 3);
  6
!

The object stored in arguments is not an array, even though it looks like one (for example, arguments.reverse() will not work). To convert arguments to an array use Array.prototype.slice.call(arguments), which will make more sense after the next section.

The value undefined is used when arguments are missing or when a function doesn't return anything:

→ function f(a, b) { return [a, b]; }
→ f(1);
  [1, undefined]
→ function g() {}
→ g();
  undefined

Every function has an associated execution context called this. You can think of this as "how we got to the function," which is the global window object for plain functions and the object before the dot for functions called on objects:

→ function func() { return this; }
→ func();
  window
→ var obj = { f: func };
→ obj.f();
  obj

The value of this can be explicitly set with the call or apply methods, which are properties of every function object. The first argument to both call and apply is the object to use as this inside the function:

→ function f(a, b) { return [this, a, b]; }
→ f(1, 2);
  [window, 1, 2]
→ f.call(window, 1, 2);
  [window, 1, 2]
→ f.apply(window, [1, 2]);
  [window, 1, 2]
→ f.call(0, 1, 2);
  [0, 1, 2]

Classes/Prototypes

While JavaScript is object-oriented, it is prototype-based rather than class-based. An object can inherit properties from another object, called its prototype. Instances retain a link to their prototype, and modifying the prototype modifies all instances of that prototype as well. Accessing a property on an object first checks for a value defined directly on that object. If there is no such definition, the object's prototype is checked next, then the prototype's prototype, and all the way up the prototype chain until the prototype for Object is reached. An object's prototype cannot be set directly and is fixed at construction time. Although most JavaScript engines will allow you to access and modify it directly via the __proto__ property, that property still non-standard and doesn't work in IE.

Java-like classes can still be emulated on top of prototypes. There are many different ways to do this and we will only cover one method here, although it is likely to be one of the more efficient methods in terms of memory and speed. Before we do that, however, we need to know how to set an object's prototype. There are two ways to do this, either the Object.create()1 method or the new keyword. The core functionality of Object.create() is to create an empty object with its prototype set to the first argument:

→ var a = { value: 1 };
→ var b = Object.create(a);
→ b.value;
  1
→ a.value = 2;
→ b.value;
  2

The other way of setting the prototype of an object is using the new keyword. This is where it starts to get weird. Any function can serve as an object constructor:

→ function TestClass(value) {
    this.number = value;
  }
→ var t = new TestClass(1);
→ t.number;
  1
→ t instanceof TestClass;
  true

Every function has a prototype property that is initially set to an empty object. The new keyword in the above code creates a new object, sets the prototype of that object to TestClass.prototype, and calls the TestClass function with this set to the new instance. If we were implementing new manually, that would look something like this:

  // This implements "var t = new TestClass(1);"
→ var t = Object.create(TestClass.prototype);
→ TestClass.call(t, 1);

This allows a direct mapping of class concepts to prototype:

Java
public class Vector {
  public double x;
  public double y;

  public Vector(double x, double y) {
    this.x = x;
    this.y = y;
  }

  public Vector add(Vector other) {
    return new Vector(x + other.x, y + other.y);
  }
}
JavaScript
function Vector(x, y) {
  this.x = x;
  this.y = y;
}

Vector.prototype.add = function(other) {
  return new Vector(this.x + other.x, this.y + other.y);
};

Because of how this works, we don't actually need prototypes to create a singleton. We can just create them directly using an object literal:

→ var singleton = {
    nextID: 0,
    getID: function() {
      return this.nextID++;
    }
  };
→ singleton.getID();
  0
→ singleton.getID();
  1
!

JavaScript code is allowed to modify the prototype of any object, including built-in objects like Array and even Object. This is considered bad practice because it may break some libraries. For example, properties added to Object.prototype will show up in for loops:

→ Object.prototype.problem = true;
→ var obj = { a: 1, b: 2 };
→ var names = [];
→ for (var x in obj) {
    names.push(x);
  }
→ names;
  ["a", "b", "problem"]

The way to avoid this is to use the hasOwnProperty() method, which returns true only when the first argument is the name of a property defined directly on that object:

→ Object.prototype.problem = true;
→ var obj = { a: 1, b: 2 };
→ var names = [];
→ for (var x in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, x)) {
      names.push(x);
    }
  }
→ names;
  ["a", "b"]

Note that this code does not call obj.hasOwnProperty(x) but rather Object.prototype.hasOwnProperty.call(obj, x). This avoids problems when the hasOwnProperty property of obj has been set to something else.

Subclasses

To create subclasses, we need to extend the prototype chain of the subclass to include the superclass. Recall that the prototype for an existing object is inaccessible in JavaScript so we cannot set it directly (excluding non-standard extensions like __proto__), but we can set the prototype at creation time via either Object.create() or new. The key to subclassing is to set Subclass.prototype = Object.create(Superclass.prototype):

Java
public class Field {
  public String value;
  public static String FONT = "Helvetica";

  public Field(String value) {
    this.value = value;
  }

  public String getHTML() {
    return "<input value=\"" + sanitize(value) + "\">";
  }

  public boolean validate() {
    return true;
  }

  public static Font getFont() {
    return new Font(FONT);
  }
}

public class EmailField extends Field {
  public String domain;

  public EmailField(String value, String domain) {
    super(value);
    this.domain = domain;
  }

  public boolean validate() {
    return super.validate() &&
      isValidEmail(this.value, this.domain);
  }
}
JavaScript
function Field(value) {
  this.value = value;
}

Field.FONT = "Helvetica";

Field.prototype.getHTML = function() {
  return "<input value=\"" + sanitize(this.value) + "\">";
}

Field.prototype.validate = function() {
  return true;
};

Field.getFont = function() {
  return new Font(Field.FONT);
};

function EmailField(value, domain) {
  Field.call(this, value);
  this.domain = domain;
}

EmailField.prototype = Object.create(Field.prototype);

EmailField.prototype.validate = function() {
  return Field.prototype.validate.call(this) &&
    isValidEmail(this.value, this.domain);
};

Tricks

This section contains common JavaScript syntactic shortcuts that you will likely see in the wild. While they are more concise, they will likely be confusing to people reading your code who are not familiar with JavaScript, so use them with caution.

The short-circuit || operator is often used to provide defaults for optional values that may be undefined. This operand evaluates to the right operand if the left operand is false, null, undefined, "", or 0, otherwise it evaluates to the left operand. In the example below, options.host and options.port are undefined when not specified, and so revert to their defaults via the || operator:

→ function createURL(path, options) {
    options = options || {};
    var host = options.host || "localhost";
    var port = options.port || 80;
    return { path: path, host: host, port: port };
  }
→ createURL("/");
  { path: "/", host: "localhost", port: 80 }
→ createURL("/dev", { port: 8080 });
  { path: "/dev", host: "localhost", port: 8080 }

The short-circuit && operator is sometimes used to guard against values that may be null. This operand evaluates to the left operand if the left operand is false, null, undefined, "", or 0, otherwise it evaluates to the right operand. The code below uses the && operator to avoid accessing the name property of null (which would cause an error):

→ function getName(obj) { return obj && obj.name; }
→ getName(null);
  null
→ getName({ name: "Steve" });
  "Steve"

Casting is sometimes done using the unary + operator or a double application of the unary ! operator. These cast to a Number or a Boolean, respectively:

→ function test(value) {
    return [Number(value), +value, Boolean(value), !!value];
  }
→ test("52");
  [52, 52, true, true]
→ test(null);
  [0, 0, false, false]

1 Note that Object.create() isn't available in versions of IE before IE 9. Luckily we can emulate it using new, the other way to set an object's prototype. While Object.create() has more functionality than we used in this article, this replacement will cover setting the prototype:

→ Object.create = Object.create || function(proto) {
    function proxy() {}
    proxy.prototype = proto;
    return new proxy();
  };

Written by Evan Wallace in 2011.