buy the book to support the author.

Chapter 1. Basic JavaScript

This chapter is about “Basic JavaScript,” a name I chose for a subset of JavaScript that is as concise as possible while still enabling you to be productive. When you are starting to learn JavaScript, I recommend that you program in it for a while before moving on to the rest of the language. That way, you don’t have to learn everything at once, which can be confusing.

Background

This section gives a little background on JavaScript to help you understand why it is the way it is.

JavaScript Versus ECMAScript

ECMAScript is the official name for JavaScript. A new name became necessary because there is a trademark on JavaScript (held originally by Sun, now by Oracle). At the moment, Mozilla is one of the few companies allowed to officially use the name JavaScript because it received a license long ago. For common usage, these rules apply:

  • JavaScript means the programming language.
  • ECMAScript is the name used by the language specification. Therefore, whenever referring to versions of the language, people say ECMAScript. The current version of JavaScript is ECMAScript 5; ECMAScript 6 is currently being developed.

Influences and Nature of the Language

JavaScript’s creator, Brendan Eich, had no choice but to create the language very quickly (or other, worse technologies would have been adopted by Netscape). He borrowed from several programming languages: Java (syntax, primitive values versus objects), Scheme and AWK (first-class functions), Self (prototypal inheritance), and Perl and Python (strings, arrays, and regular expressions).

JavaScript did not have exception handling until ECMAScript 3, which explains why the language so often automatically converts values and so often fails silently: it initially couldn’t throw exceptions.

On one hand, JavaScript has quirks and is missing quite a bit of functionality (block-scoped variables, modules, support for subclassing, etc.). On the other hand, it has several powerful features that allow you to work around these problems. In other languages, you learn language features. In JavaScript, you often learn patterns instead.

Given its influences, it is no surprise that JavaScript enables a programming style that is a mixture of functional programming (higher-order functions; built-in map, reduce, etc.) and object-oriented programming (objects, inheritance).

Syntax

This section explains basic syntactic principles of JavaScript.

An Overview of the Syntax

A few examples of syntax:

  1. // Two slashes start single-line comments
  2.  
  3. var x; // declaring a variable
  4.  
  5. x = 3 + y; // assigning a value to the variable `x`
  6.  
  7. foo(x, y); // calling function `foo` with parameters `x` and `y`
  8. obj.bar(3); // calling method `bar` of object `obj`
  9.  
  10. // A conditional statement
  11. if (x === 0) { // Is `x` equal to zero?
  12. x = 123;
  13. }
  14.  
  15. // Defining function `baz` with parameters `a` and `b`
  16. function baz(a, b) {
  17. return a + b;
  18. }

Note the two different uses of the equals sign:

  • A single equals sign (=) is used to assign a value to a variable.
  • A triple equals sign (===) is used to compare two values (see Equality Operators).

Statements Versus Expressions

To understand JavaScript’s syntax, you should know that it has two major syntactic categories: statements and expressions:

  • Statements “do things.” A program is a sequence of statements. Here is an example of a statement, which declares (creates) a variable foo:
  1. var foo;
  • Expressions produce values. They are function arguments, the right side of an assignment, etc. Here’s an example of an expression:
  1. 3 * 7

The distinction between statements and expressions is best illustrated by the fact that JavaScript has two different ways to do if-then-else—either as a statement:

  1. var x;
  2. if (y >= 0) {
  3. x = y;
  4. } else {
  5. x = -y;
  6. }

or as an expression:

  1. var x = y >= 0 ? y : -y;

You can use the latter as a function argument (but not the former):

  1. myFunction(y >= 0 ? y : -y)

Finally, wherever JavaScript expects a statement, you can also use an expression; for example:

  1. foo(7, 1);

The whole line is a statement (a so-called expression statement), but the function call foo(7, 1) is an expression.

Semicolons

Semicolons are optional in JavaScript. However, I recommend always including them, because otherwise JavaScript can guess wrong about the end of a statement. The details are explained in Automatic Semicolon Insertion.

Semicolons terminate statements, but not blocks. There is one case where you will see a semicolon after a block: a function expression is an expression that ends with a block. If such an expression comes last in a statement, it is followed by a semicolon:

  1. // Pattern: var _ = ___;
  2. var x = 3 * 7;
  3. var f = function () { }; // function expr. inside var decl.

Comments

JavaScript has two kinds of comments: single-line comments and multiline comments. Single-line comments start with // and are terminated by the end of the line:

  1. x++; // single-line comment

Multiline comments are delimited by / and /:

  1. /* This is
  2. a multiline
  3. comment.
  4. */

Variables and Assignment

Variables in JavaScript are declared before they are used:

  1. var foo; // declare variable `foo`

Assignment

You can declare a variable and assign a value at the same time:

  1. var foo = 6;

You can also assign a value to an existing variable:

  1. foo = 4; // change variable `foo`

Compound Assignment Operators

There are compound assignment operators such as +=. The following twoassignments are equivalent:

  1. x += 1;
  2. x = x + 1;

Identifiers and Variable Names

Identifiers are names that play various syntactic roles in JavaScript. For example, the name of a variable is an identifier. Identifiers are case sensitive.

Roughly, the first character of an identifier can be any Unicode letter, a dollar sign ($), or an underscore (_). Subsequent characters can additionally be any Unicode digit. Thus, the following are all legal identifiers:

  1. arg0
  2. _tmp
  3. $elem
  4. π

The following identifiers are reserved words—they are part of the syntax and can’t be used as variable names (including function names and parameter names):

arguments break case catch
class const continue debugger
default delete do else
enum export extends false
finally for function if
implements import in instanceof
interface let new null
package private protected public
return static super switch
this throw true try
typeof var void while

The following three identifiers are not reserved words, but you should treat them as if they were:

Infinity
NaN
undefined

Lastly, you should also stay away from the names of standard global variables (see Chapter 23). You can use them for local variables without breaking anything, but your code still becomes confusing.

Values

JavaScript has many values that we have come to expect from programming languages: booleans, numbers, strings, arrays, and so on. All values in JavaScript have properties.[1] Each property has a key (or name) and a value. You can think of properties like fields of a record. You use the dot (.) operator to read a property:

  1. value.propKey

For example, the string 'abc' has the property length:

  1. > var str = 'abc';
  2. > str.length
  3. 3

The preceding can also be written as:

  1. > 'abc'.length
  2. 3

The dot operator is also used to assign a value to a property:

  1. > var obj = {}; // empty object
  2. > obj.foo = 123; // create property `foo`, set it to 123
  3. 123
  4. > obj.foo
  5. 123

And you can use it to invoke methods:

  1. > 'hello'.toUpperCase()
  2. 'HELLO'

In the preceding example, we have invoked the method toUpperCase() on the value'hello'.

Primitive Values Versus Objects

JavaScript makes a somewhat arbitrary distinction between values:

  • The primitive values are booleans, numbers, strings, null, and undefined.
  • All other values are objects.

A major difference between the two is how they are compared; eachobject has a unique identity and is only (strictly) equal to itself:

  1. > var obj1 = {}; // an empty object
  2. > var obj2 = {}; // another empty object
  3. > obj1 === obj2
  4. false
  5. > obj1 === obj1
  6. true

In contrast, all primitive values encoding the same value are consideredthe same:

  1. > var prim1 = 123;
  2. > var prim2 = 123;
  3. > prim1 === prim2
  4. true

The next two sections explain primitive values and objects in more detail.

Primitive Values

The following are all of the primitive values (or primitives for short):

Primitives have the following characteristics:

  • Compared by value
  • The “content” is compared:
  1. > 3 === 3
  2. true
  3. > 'abc' === 'abc'
  4. true
  • Always immutable
  • Properties can’t be changed, added, or removed:
  1. > var str = 'abc';
  2.  
  3. > str.length = 1; // try to change property `length`
  4. > str.length // ⇒ no effect
  5. 3
  6.  
  7. > str.foo = 3; // try to create property `foo`
  8. > str.foo // ⇒ no effect, unknown property
  9. undefined

(Reading an unknown property always returns undefined.)

Objects

All nonprimitive values are objects. The most common kinds of objects are:

  • Plain objects, which can be created by object literals (see Single Objects):
  1. {
  2. firstName: 'Jane',
  3. lastName: 'Doe'
  4. }

The preceding object has two properties: the value of property firstNameis 'Jane' and the value of property lastName is 'Doe'.

  • Arrays, which can be created by array literals (see Arrays):
  1. [ 'apple', 'banana', 'cherry' ]

The preceding array has three elements that can be accessed via numericindices. For example, the index of 'apple' is 0.

  • Regular expressions, which can be created by regular expression literals (see Regular Expressions):
  1. /^a+b+$/

Objects have the following characteristics:

  • Compared by reference
  • Identities are compared; every value has its own identity:
  1. > ({} === {}) // two different empty objects
  2. false
  3.  
  4. > var obj1 = {};
  5. > var obj2 = obj1;
  6. > obj1 === obj2
  7. true
  • Mutable by default
  • You can normally freely change, add, and remove properties (see Single Objects):
  1. > var obj = {};
  2. > obj.foo = 123; // add property `foo`
  3. > obj.foo
  4. 123

undefined and null

Most programming languages have values denoting missing information. JavaScript has two such “nonvalues,” undefined and null:

  • undefined means “no value.” Uninitialized variables are undefined:
  1. > var foo;
  2. > foo
  3. undefined

Missing parameters are undefined:

  1. > function f(x) { return x }
  2. > f()
  3. undefined

If you read a nonexistent property, you get undefined:

  1. > var obj = {}; // empty object
  2. > obj.foo
  3. undefined
  • null means “no object.” It is used as a nonvalue whenever an object is expected (parameters, last in a chain of objects, etc.).

Warning

undefined and null have no properties, not even standard methods such as toString().

Checking for undefined or null

Functions normally allow you to indicate a missing value via either undefined or null. You can do the same via an explicit check:

  1. if (x === undefined || x === null) {
  2. ...
  3. }

You can also exploit the fact that both undefined and null are considered false:

  1. if (!x) {
  2. ...
  3. }

Warning

false, 0, NaN, and '' are also considered false (see Truthy and Falsy).

Categorizing Values Using typeof and instanceof

There are two operators for categorizing values: typeof is mainly used for primitive values, while instanceof is used for objects.

typeof looks like this:

  1. typeof value

It returns a string describing the “type” of value. Here are some examples:

  1. > typeof true
  2. 'boolean'
  3. > typeof 'abc'
  4. 'string'
  5. > typeof {} // empty object literal
  6. 'object'
  7. > typeof [] // empty array literal
  8. 'object'

The following table lists all results of typeof:

Operand Result
undefined 'undefined'
null 'object'
Boolean value 'boolean'
Number value 'number'
String value 'string'
Function 'function'
All other normal values 'object'
(Engine-created value) JavaScript engines are allowed to create values for which typeof returns arbitrary strings (different from all results listed in this table).

typeof null returning 'object' is a bug that can’t be fixed, because it would break existing code. It does not mean that null is an object.

instanceof looks like this:

  1. value instanceof Constr

It returns true if value is an object that has been created by theconstructor Constr (see Constructors: Factories for Objects). Here are some examples:

  1. > var b = new Bar(); // object created by constructor Bar
  2. > b instanceof Bar
  3. true
  4.  
  5. > {} instanceof Object
  6. true
  7. > [] instanceof Array
  8. true
  9. > [] instanceof Object // Array is a subconstructor of Object
  10. true
  11.  
  12. > undefined instanceof Object
  13. false
  14. > null instanceof Object
  15. false

Booleans

The primitive boolean type comprises the values true and false. The following operators produce booleans:

  • Binary logical operators: && (And), || (Or)
  • Prefix logical operator: ! (Not)
  • Comparison operators:
  • Equality operators: ===, !==, ==, !=
  • Ordering operators (for strings and numbers): >, >=, <, <=

Truthy and Falsy

Whenever JavaScript expects a boolean value (e.g., for the condition of an if statement), any value can be used. It will be interpreted as either true or false. The following values are interpreted as false:

  • undefined, null
  • Boolean: false
  • Number: 0, NaN
  • String: ''

All other values (including all objects!) are considered true. Values interpreted as false are called falsy, and values interpreted as true are called truthy. Boolean(), called as a function, converts its parameter to a boolean. You can use it to test how a value is interpreted:

  1. > Boolean(undefined)
  2. false
  3. > Boolean(0)
  4. false
  5. > Boolean(3)
  6. true
  7. > Boolean({}) // empty object
  8. true
  9. > Boolean([]) // empty array
  10. true

Binary Logical Operators

Binary logical operators in JavaScript are short-circuiting. That is, if the first operand suffices for determining the result, the second operand is not evaluated. For example, in the following expressions, the function foo() is never called:

  1. false && foo()
  2. true || foo()

Furthermore, binary logical operators return either one of their operands—which may or may not be a boolean. A check for truthiness is used to determine which one:

  • And (&&)
  • If the first operand is falsy, return it. Otherwise, return the second operand:
  1. > NaN && 'abc'
  2. NaN
  3. > 123 && 'abc'
  4. 'abc'
  • Or (||)
  • If the first operand is truthy, return it. Otherwise, return the second operand:
  1. > 'abc' || 123
  2. 'abc'
  3. > '' || 123
  4. 123

Equality Operators

JavaScript has two kinds of equality:

  • Normal, or “lenient,” (in)equality: == and !=
  • Strict (in)equality: === and !==

Normal equality considers (too) many values to be equal (the details are explained in Normal (Lenient) Equality (==, !=)), which can hide bugs. Therefore, always using strict equality is recommended.

Numbers

All numbers in JavaScript are floating-point:

  1. > 1 === 1.0
  2. true

Special numbers include the following:

  • NaN (“not a number”)
  • An error value:
  1. > Number('xyz') // 'xyz' can’t be converted to a number
  2. NaN
  • Infinity
  • Also mostly an error value:
  1. > 3 / 0
  2. Infinity
  3. > Math.pow(2, 1024) // number too large
  4. Infinity

Infinity is larger than any other number (except NaN). Similarly, -Infinity is smaller than any other number (except NaN). That makes these numbers useful as default values (e.g., when you are looking for a minimum or a maximum).

Operators

JavaScript has the following arithmetic operators (see Arithmetic Operators):

  • Addition: number1 + number2
  • Subtraction: number1 - number2
  • Multiplication: number1 * number2
  • Division: number1 / number2
  • Remainder: number1 % number2
  • Increment: ++variable, variable++
  • Decrement: —variable, variable—
  • Negate: -value
  • Convert to number: +value

The global object Math (see Math) provides more arithmetic operations, via functions.

JavaScript also has operators for bitwise operations (e.g., bitwise And; see Bitwise Operators).

Strings

Strings can be created directly via string literals. Those literals are delimited by single or double quotes. The backslash (\) escapes characters and produces a few control characters. Here are some examples:

  1. 'abc'
  2. "abc"
  3.  
  4. 'Did she say "Hello"?'
  5. "Did she say \"Hello\"?"
  6.  
  7. 'That\'s nice!'
  8. "That's nice!"
  9.  
  10. 'Line 1\nLine 2' // newline
  11. 'Backlash: \\'

Single characters are accessed via square brackets:

  1. > var str = 'abc';
  2. > str[1]
  3. 'b'

The property length counts the number of characters in the string:

  1. > 'abc'.length
  2. 3

Like all primitives, strings are immutable; you need to create a new string if you want to change an existing one.

String Operators

Strings are concatenated via the plus (+) operator, which converts the other operand to a string if one of the operands is a string:

  1. > var messageCount = 3;
  2. > 'You have ' + messageCount + ' messages'
  3. 'You have 3 messages'

To concatenate strings in multiple steps, use the += operator:

  1. > var str = '';
  2. > str += 'Multiple ';
  3. > str += 'pieces ';
  4. > str += 'are concatenated.';
  5. > str
  6. 'Multiple pieces are concatenated.'

String Methods

Strings have many useful methods (see String Prototype Methods). Here are some examples:

  1. > 'abc'.slice(1) // copy a substring
  2. 'bc'
  3. > 'abc'.slice(1, 2)
  4. 'b'
  5.  
  6. > '\t xyz '.trim() // trim whitespace
  7. 'xyz'
  8.  
  9. > 'mjölnir'.toUpperCase()
  10. 'MJÖLNIR'
  11.  
  12. > 'abc'.indexOf('b') // find a string
  13. 1
  14. > 'abc'.indexOf('x')
  15. -1

Statements

Conditionals and loops in JavaScript are introduced in the following sections.

Conditionals

The if statement has a then clause and an optional else clause that are executed depending on a boolean condition:

  1. if (myvar === 0) {
  2. // then
  3. }
  4.  
  5. if (myvar === 0) {
  6. // then
  7. } else {
  8. // else
  9. }
  10.  
  11. if (myvar === 0) {
  12. // then
  13. } else if (myvar === 1) {
  14. // else-if
  15. } else if (myvar === 2) {
  16. // else-if
  17. } else {
  18. // else
  19. }

I recommend always using braces (they denote blocks of zero or more statements). But you don’t have to do so if a clause is only a single statement (the same holds for the control flow statements for and while):

  1. if (x < 0) return -x;

The following is a switch statement. The value of fruit decides whichcase is executed:

  1. switch (fruit) {
  2. case 'banana':
  3. // ...
  4. break;
  5. case 'apple':
  6. // ...
  7. break;
  8. default: // all other cases
  9. // ...
  10. }

The “operand” after case can be any expression; it is compared via === with the parameter of switch.

Loops

The for loop has the following format:

  1. for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
  2. «statement»

init is executed at the beginning of the loop. condition is checked before each loop iteration; if it becomes false, then the loop is terminated. post_iteration is executed after each loop iteration.

This example prints all elements of the array arr on the console:

  1. for (var i=0; i < arr.length; i++) {
  2. console.log(arr[i]);
  3. }

The while loop continues looping over its body while its condition holds:

  1. // Same as for loop above:
  2. var i = 0;
  3. while (i < arr.length) {
  4. console.log(arr[i]);
  5. i++;
  6. }

The do-while loop continues looping over its body while its condition holds. As the condition follows the body, the body is always executed at least once:

  1. do {
  2. // ...
  3. } while (condition);

In all loops:

  • break leaves the loop.
  • continue starts a new loop iteration.

Functions

One way of defining a function is via a function declaration:

  1. function add(param1, param2) {
  2. return param1 + param2;
  3. }

The preceding code defines a function, add, that has two parameters, param1 and param2, and returns the sum of both parameters. This is how you call that function:

  1. > add(6, 1)
  2. 7
  3. > add('a', 'b')
  4. 'ab'

Another way of defining add() is by assigning a function expression to a variable add:

  1. var add = function (param1, param2) {
  2. return param1 + param2;
  3. };

A function expression produces a value and can thus be used to directly pass functions as arguments to other functions:

  1. someOtherFunction(function (p1, p2) { ... });

Function Declarations Are Hoisted

Function declarations are hoisted—moved in their entirety to thebeginning of the current scope. That allows you to refer to functionsthat are declared later:

  1. function foo() {
  2. bar(); // OK, bar is hoisted
  3. function bar() {
  4. ...
  5. }
  6. }

Note that while var declarations are also hoisted (see Variables Are Hoisted), assignments performed by them are not:

  1. function foo() {
  2. bar(); // Not OK, bar is still undefined
  3. var bar = function () {
  4. // ...
  5. };
  6. }

The Special Variable arguments

You can call any function in JavaScript with an arbitrary amount of arguments; the language will never complain. It will, however, make all parameters available via the special variable arguments. arguments looks like an array, but has none of the array methods:

  1. > function f() { return arguments }
  2. > var args = f('a', 'b', 'c');
  3. > args.length
  4. 3
  5. > args[0] // read element at index 0
  6. 'a'

Too Many or Too Few Arguments

Let’s use the following function to explore how too many or too fewparameters are handled in JavaScript (the function toArray() is shown in Converting arguments to an Array):

  1. function f(x, y) {
  2. console.log(x, y);
  3. return toArray(arguments);
  4. }

Additional parameters will be ignored (except by arguments):

  1. > f('a', 'b', 'c')
  2. a b
  3. [ 'a', 'b', 'c' ]

Missing parameters will get the value undefined:

  1. > f('a')
  2. a undefined
  3. [ 'a' ]
  4. > f()
  5. undefined undefined
  6. []

Optional Parameters

The following is a common pattern for assigning default values to parameters:

  1. function pair(x, y) {
  2. x = x || 0; // (1)
  3. y = y || 0;
  4. return [ x, y ];
  5. }

In line (1), the || operator returns x if it is truthy (not null, undefined, etc.). Otherwise, it returns the second operand:

  1. > pair()
  2. [ 0, 0 ]
  3. > pair(3)
  4. [ 3, 0 ]
  5. > pair(3, 5)
  6. [ 3, 5 ]

Enforcing an Arity

If you want to enforce an arity (a specific number of parameters), you can check arguments.length:

  1. function pair(x, y) {
  2. if (arguments.length !== 2) {
  3. throw new Error('Need exactly 2 arguments');
  4. }
  5. ...
  6. }

Converting arguments to an Array

arguments is not an array, it is only array-like (see Array-Like Objects and Generic Methods). It has a property length, and you can access its elements via indices in square brackets. You cannot, however, remove elements or invoke any of the array methods on it. Thus, you sometimes need to convert arguments to an array, which is what the following function does (it is explained in Array-Like Objects and Generic Methods):

  1. function toArray(arrayLikeObject) {
  2. return Array.prototype.slice.call(arrayLikeObject);
  3. }

Exception Handling

The most common way to handle exceptions (see Chapter 14) is as follows:

  1. function getPerson(id) {
  2. if (id < 0) {
  3. throw new Error('ID must not be negative: '+id);
  4. }
  5. return { id: id }; // normally: retrieved from database
  6. }
  7.  
  8. function getPersons(ids) {
  9. var result = [];
  10. ids.forEach(function (id) {
  11. try {
  12. var person = getPerson(id);
  13. result.push(person);
  14. } catch (exception) {
  15. console.log(exception);
  16. }
  17. });
  18. return result;
  19. }

The try clause surrounds critical code, and the catch clause is executed if an exception is thrown inside the try clause. Using the preceding code:

  1. > getPersons([2, -5, 137])
  2. [Error: ID must not be negative: -5]
  3. [ { id: 2 }, { id: 137 } ]

Strict Mode

Strict mode (see Strict Mode) enables more warnings and makes JavaScript a cleaner language (nonstrict mode is sometimes called “sloppy mode”). To switch it on, type the following line first in a JavaScript file or a <script> tag:

  1. 'use strict';

You can also enable strict mode per function:

  1. function functionInStrictMode() {
  2. 'use strict';
  3. }

Variable Scoping and Closures

In JavaScript, you declare variables via var before using them:

  1. > var x;
  2. > x
  3. undefined
  4. > y
  5. ReferenceError: y is not defined

You can declare and initialize several variables with a single varstatement:

  1. var x = 1, y = 2, z = 3;

But I recommend using one statement per variable (the reason is explained in Syntax). Thus, I would rewrite the previous statement to:

  1. var x = 1;
  2. var y = 2;
  3. var z = 3;

Because of hoisting (see Variables Are Hoisted), it is usually best to declare variables at the beginning of a function.

Variables Are Function-Scoped

The scope of a variable is always the complete function (as opposed tothe current block). For example:

  1. function foo() {
  2. var x = -512;
  3. if (x < 0) { // (1)
  4. var tmp = -x;
  5. ...
  6. }
  7. console.log(tmp); // 512
  8. }

We can see that the variable tmp is not restricted to the blockstarting in line (1); it exists until the end of the function.

Variables Are Hoisted

Each variable declaration is hoisted: the declaration is moved to the beginning of the function, but assignments that it makes stay put. As an example, consider the variable declaration in line (1) in the following function:

  1. function foo() {
  2. console.log(tmp); // undefined
  3. if (false) {
  4. var tmp = 3; // (1)
  5. }
  6. }

Internally, the preceding function is executed like this:

  1. function foo() {
  2. var tmp; // hoisted declaration
  3. console.log(tmp);
  4. if (false) {
  5. tmp = 3; // assignment stays put
  6. }
  7. }

Closures

Each function stays connected to the variables of the functions thatsurround it, even after it leaves the scope in which it was created. Forexample:

  1. function createIncrementor(start) {
  2. return function () { // (1)
  3. start++;
  4. return start;
  5. }
  6. }

The function starting in line (1) leaves the context in which it was created, but stays connected to a live version of start:

  1. > var inc = createIncrementor(5);
  2. > inc()
  3. 6
  4. > inc()
  5. 7
  6. > inc()
  7. 8

A closure is a function plus the connection to the variables of itssurrounding scopes. Thus, what createIncrementor() returns is aclosure.

The IIFE Pattern: Introducing a New Scope

Sometimes you want to introduce a new variable scope—for example, to prevent a variable from becoming global. In JavaScript, you can’t use a block to do so; you must use a function. But there is a pattern for using a function in a block-like manner. It is called IIFE (immediately invoked function expression, pronounced “iffy”):

  1. (function () { // open IIFE
  2. var tmp = ...; // not a global variable
  3. }()); // close IIFE

Be sure to type the preceding example exactly as shown (apart from the comments). An IIFE is a function expression that is called immediately after you define it. Inside the function, a new scope exists, preventing tmp from becoming global. Consult Introducing a New Scope via an IIFE for details on IIFEs.

IIFE use case: inadvertent sharing via closures

Closures keep their connections to outer variables, which is sometimes not what you want:

  1. var result = [];
  2. for (var i=0; i < 5; i++) {
  3. result.push(function () { return i }); // (1)
  4. }
  5. console.log(result[1]()); // 5 (not 1)
  6. console.log(result[3]()); // 5 (not 3)

The value returned in line (1) is always the current value of i, not the value it had when the function was created. After the loop is finished, i has the value 5, which is why all functions in the array return that value. If you want the function in line (1) to receive a snapshot of the current value of i, you can use an IIFE:

  1. for (var i=0; i < 5; i++) {
  2. (function () {
  3. var i2 = i; // copy current i
  4. result.push(function () { return i2 });
  5. }());
  6. }

Objects and Constructors

This section covers two basic object-oriented mechanisms of JavaScript: single objects and constructors (which are factories for objects, similar to classes in other languages).

Single Objects

Like all values, objects have properties. You could, in fact, consider an object to be a set of properties, where each property is a (key, value) pair. The key is a string, and the value is any JavaScript value.

In JavaScript, you can directly create plain objects, via object literals:

  1. 'use strict';
  2. var jane = {
  3. name: 'Jane',
  4.  
  5. describe: function () {
  6. return 'Person named '+this.name;
  7. }
  8. };

The preceding object has the properties name and describe. You can read (“get”) and write (“set”) properties:

  1. > jane.name // get
  2. 'Jane'
  3. > jane.name = 'John'; // set
  4. > jane.newProperty = 'abc'; // property created automatically

Function-valued properties such as describe are called methods. They use this to refer to the object that was used to call them:

  1. > jane.describe() // call method
  2. 'Person named John'
  3. > jane.name = 'Jane';
  4. > jane.describe()
  5. 'Person named Jane'

The in operator checks whether a property exists:

  1. > 'newProperty' in jane
  2. true
  3. > 'foo' in jane
  4. false

If you read a property that does not exist, you get the value undefined. Hence, the previous two checks could also be performed like this:[2]

  1. > jane.newProperty !== undefined
  2. true
  3. > jane.foo !== undefined
  4. false

The delete operator removes a property:

  1. > delete jane.newProperty
  2. true
  3. > 'newProperty' in jane
  4. false

Arbitrary Property Keys

A property key can be any string. So far, we have seen property keys in object literals and after the dot operator. However, you can use them that way only if they are identifiers (see Identifiers and Variable Names). If you want to use other strings as keys, you have to quote them in an object literal and use square brackets to get and set the property:

  1. > var obj = { 'not an identifier': 123 };
  2. > obj['not an identifier']
  3. 123
  4. > obj['not an identifier'] = 456;

Square brackets also allow you to compute the key of a property:

  1. > var obj = { hello: 'world' };
  2. > var x = 'hello';
  3.  
  4. > obj[x]
  5. 'world'
  6. > obj['hel'+'lo']
  7. 'world'

Extracting Methods

If you extract a method, it loses its connection with the object. On its own, the function is not a method anymore, and this has the value undefined (in strict mode).

As an example, let’s go back to the earlier object jane:

  1. 'use strict';
  2. var jane = {
  3. name: 'Jane',
  4.  
  5. describe: function () {
  6. return 'Person named '+this.name;
  7. }
  8. };

We want to extract the method describe from jane, put it into a variable func, and call it. However, that doesn’t work:

  1. > var func = jane.describe;
  2. > func()
  3. TypeError: Cannot read property 'name' of undefined

The solution is to use the method bind() that all functions have. It creates a new function whose this always has the given value:

  1. > var func2 = jane.describe.bind(jane);
  2. > func2()
  3. 'Person named Jane'

Functions Inside a Method

Every function has its own special variable this. This is inconvenient if you nest a function inside a method, because you can’t access the method’s this from the function. The following is an example where we call forEach with a function to iterate over an array:

  1. var jane = {
  2. name: 'Jane',
  3. friends: [ 'Tarzan', 'Cheeta' ],
  4. logHiToFriends: function () {
  5. 'use strict';
  6. this.friends.forEach(function (friend) {
  7. // `this` is undefined here
  8. console.log(this.name+' says hi to '+friend);
  9. });
  10. }
  11. }

Calling logHiToFriends produces an error:

  1. > jane.logHiToFriends()
  2. TypeError: Cannot read property 'name' of undefined

Let’s look at two ways of fixing this. First, we could store this in adifferent variable:

  1. logHiToFriends: function () {
  2. 'use strict';
  3. var that = this;
  4. this.friends.forEach(function (friend) {
  5. console.log(that.name+' says hi to '+friend);
  6. });
  7. }

Or, forEach has a second parameter that allows you to provide avalue for this:

  1. logHiToFriends: function () {
  2. 'use strict';
  3. this.friends.forEach(function (friend) {
  4. console.log(this.name+' says hi to '+friend);
  5. }, this);
  6. }

Function expressions are often used as arguments in function calls in JavaScript. Always be careful when you refer to this from one of those function expressions.

Constructors: Factories for Objects

Until now, you may think that JavaScript objects are only maps from strings to values, a notion suggested by JavaScript’s object literals, which look like the map/dictionary literals of other languages. However, JavaScript objects also support a feature that is truly object-oriented: inheritance. This section does not fully explain how JavaScript inheritance works, but it shows you a simple pattern to get you started. Consult Chapter 17 if you want to know more.

In addition to being “real” functions and methods, functions play another role in JavaScript: they become constructors—factories for objects—if invoked via the new operator. Constructors are thus a rough analog to classes in other languages. By convention, the names of constructors start with capital letters. For example:

  1. // Set up instance data
  2. function Point(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. // Methods
  7. Point.prototype.dist = function () {
  8. return Math.sqrt(this.x*this.x + this.y*this.y);
  9. };

We can see that a constructor has two parts. First, the function Point sets up the instance data. Second, the property Point.prototype contains an object with the methods. The former data is specific to each instance, while the latter data is shared among all instances.

To use Point, we invoke it via the new operator:

  1. > var p = new Point(3, 5);
  2. > p.x
  3. 3
  4. > p.dist()
  5. 5.830951894845301

p is an instance of Point:

  1. > p instanceof Point
  2. true

Arrays

Arrays are sequences of elements that can be accessed via integer indices starting at zero.

Array Literals

Array literals are handy for creating arrays:

  1. > var arr = [ 'a', 'b', 'c' ];

The preceding array has three elements: the strings 'a', 'b', and 'c'. You can access them via integer indices:

  1. > arr[0]
  2. 'a'
  3. > arr[0] = 'x';
  4. > arr
  5. [ 'x', 'b', 'c' ]

The length property indicates how many elements an array has. You can use it to append elements and to remove elements:

  1. > var arr = ['a', 'b'];
  2. > arr.length
  3. 2
  4.  
  5. > arr[arr.length] = 'c';
  6. > arr
  7. [ 'a', 'b', 'c' ]
  8. > arr.length
  9. 3
  10.  
  11. > arr.length = 1;
  12. > arr
  13. [ 'a' ]

The in operator works for arrays, too:

  1. > var arr = [ 'a', 'b', 'c' ];
  2. > 1 in arr // is there an element at index 1?
  3. true
  4. > 5 in arr // is there an element at index 5?
  5. false

Note that arrays are objects and can thus have object properties:

  1. > var arr = [];
  2. > arr.foo = 123;
  3. > arr.foo
  4. 123

Array Methods

Arrays have many methods (see Array Prototype Methods). Here are a few examples:

  1. > var arr = [ 'a', 'b', 'c' ];
  2.  
  3. > arr.slice(1, 2) // copy elements
  4. [ 'b' ]
  5. > arr.slice(1)
  6. [ 'b', 'c' ]
  7.  
  8. > arr.push('x') // append an element
  9. 4
  10. > arr
  11. [ 'a', 'b', 'c', 'x' ]
  12.  
  13. > arr.pop() // remove last element
  14. 'x'
  15. > arr
  16. [ 'a', 'b', 'c' ]
  17.  
  18. > arr.shift() // remove first element
  19. 'a'
  20. > arr
  21. [ 'b', 'c' ]
  22.  
  23. > arr.unshift('x') // prepend an element
  24. 3
  25. > arr
  26. [ 'x', 'b', 'c' ]
  27.  
  28. > arr.indexOf('b') // find the index of an element
  29. 1
  30. > arr.indexOf('y')
  31. -1
  32.  
  33. > arr.join('-') // all elements in a single string
  34. 'x-b-c'
  35. > arr.join('')
  36. 'xbc'
  37. > arr.join()
  38. 'x,b,c'

Iterating over Arrays

There are several array methods for iterating over elements (see Iteration (Nondestructive)). The two most important ones are forEach and map.

forEach iterates over an array and hands the current element and its index to a function:

  1. [ 'a', 'b', 'c' ].forEach(
  2. function (elem, index) { // (1)
  3. console.log(index + '. ' + elem);
  4. });

The preceding code produces the following output:

  1. 0. a
  2. 1. b
  3. 2. c

Note that the function in line (1) is free to ignore arguments. It could, for example, only have the parameter elem.

map creates a new array by applying a function to each element of an existing array:

  1. > [1,2,3].map(function (x) { return x*x })
  2. [ 1, 4, 9 ]

Regular Expressions

JavaScript has built-in support for regular expressions (Chapter 19 refers to tutorials and explains in more detail how they work). They are delimited by slashes:

  1. /^abc$/
  2. /[A-Za-z0-9]+/

Method test(): Is There a Match?

  1. > /^a+b+$/.test('aaab')
  2. true
  3. > /^a+b+$/.test('aaa')
  4. false

Method exec(): Match and Capture Groups

  1. > /a(b+)a/.exec('_abbba_aba_')
  2. [ 'abbba', 'bbb' ]

The returned array contains the complete match at index 0, the capture of the first group at index 1, and so on. There is a way (discussed in RegExp.prototype.exec: Capture Groups) to invoke this method repeatedly to get all matches.

Method replace(): Search and Replace

  1. > '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
  2. '[a] [bbb]'

The first parameter of replace must be a regular expression with a /g flag; otherwise, only the first occurrence is replaced. There is also a way (as discussed in String.prototype.replace: Search and Replace) to use a function to compute the replacement.

Math

Math (see Chapter 21) is an object with arithmetic functions. Here are some examples:

  1. > Math.abs(-2)
  2. 2
  3.  
  4. > Math.pow(3, 2) // 3 to the power of 2
  5. 9
  6.  
  7. > Math.max(2, -1, 5)
  8. 5
  9.  
  10. > Math.round(1.9)
  11. 2
  12.  
  13. > Math.PI // pre-defined constant for π
  14. 3.141592653589793
  15.  
  16. > Math.cos(Math.PI) // compute the cosine for 180°
  17. -1

Other Functionality of the Standard Library

JavaScript’s standard library is relatively spartan, but there are more things you can use:

  • Date (Chapter 20)
  • A constructor for dates whose main functionality is parsing and creating date strings and accessing the components of a date (year, hour, etc.).
  • JSON (Chapter 22)
  • An object with functions for parsing and generating JSON data.
  • console.* methods (see The Console API)
  • These browser-specific methods are not part of the language proper, but some of them also work on Node.js.

[1] The two “non-values” undefined and null do not have properties.

[2] Note: this check will report properties as non-existent that do exist, but have the value undefined.